├── .github ├── CODEOWNERS ├── archive │ └── react-native-code-push-ci.yml └── policies │ └── resourceManagement.yml ├── .gitignore ├── .npmignore ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .watchmanconfig ├── AlertAdapter.js ├── CONTRIBUTING.md ├── CodePush.js ├── CodePush.podspec ├── Examples ├── CodePushDemoApp │ ├── .bundle │ │ └── config │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierrc.js │ ├── .watchmanconfig │ ├── App.tsx │ ├── Gemfile │ ├── Gemfile.lock │ ├── README.md │ ├── __tests__ │ │ └── App.test.tsx │ ├── android │ │ ├── app │ │ │ ├── build.gradle │ │ │ ├── debug.keystore │ │ │ ├── proguard-rules.pro │ │ │ └── src │ │ │ │ └── main │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── codepushdemoapp │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ └── MainApplication.kt │ │ │ │ └── res │ │ │ │ ├── drawable │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ └── values │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ └── settings.gradle │ ├── app.json │ ├── babel.config.js │ ├── code-push.config.example.supabase.ts │ ├── code-push.config.ts │ ├── index.js │ ├── ios │ │ ├── .xcode.env │ │ ├── CodePushDemoApp-Bridging-Header.h │ │ ├── CodePushDemoApp.xcodeproj │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata │ │ │ │ └── xcschemes │ │ │ │ └── CodePushDemoApp.xcscheme │ │ ├── CodePushDemoApp.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ ├── CodePushDemoApp │ │ │ ├── AppDelegate.swift │ │ │ ├── Images.xcassets │ │ │ │ ├── AppIcon.appiconset │ │ │ │ │ └── Contents.json │ │ │ │ └── Contents.json │ │ │ ├── Info.plist │ │ │ ├── LaunchScreen.storyboard │ │ │ └── PrivacyInfo.xcprivacy │ │ ├── Podfile │ │ └── Podfile.lock │ ├── jest.config.js │ ├── metro.config.js │ ├── package-lock.json │ ├── package.json │ ├── scripts │ │ ├── invalidateCloudfrontCache.ts │ │ └── uploadFileToS3.ts │ ├── tsconfig.json │ └── utils │ │ └── index.ts ├── create-app.js └── nexpect.js ├── LICENSE.md ├── README.md ├── Recipes ├── UpdateButton.ios.js └── UpdateOnStart.ios.js ├── SECURITY.md ├── android ├── app │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── microsoft │ │ └── codepush │ │ └── react │ │ ├── CodePush.java │ │ ├── CodePushBuilder.java │ │ ├── CodePushConstants.java │ │ ├── CodePushDialog.java │ │ ├── CodePushInstallMode.java │ │ ├── CodePushInvalidPublicKeyException.java │ │ ├── CodePushInvalidUpdateException.java │ │ ├── CodePushMalformedDataException.java │ │ ├── CodePushNativeModule.java │ │ ├── CodePushNotInitializedException.java │ │ ├── CodePushTelemetryManager.java │ │ ├── CodePushUnknownException.java │ │ ├── CodePushUpdateManager.java │ │ ├── CodePushUpdateState.java │ │ ├── CodePushUpdateUtils.java │ │ ├── CodePushUtils.java │ │ ├── DownloadProgress.java │ │ ├── DownloadProgressCallback.java │ │ ├── FileUtils.java │ │ ├── ReactInstanceHolder.java │ │ ├── SettingsManager.java │ │ └── TLSSocketFactory.java ├── build.gradle ├── codepush.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── babel-plugin-code-push ├── index.js ├── package-lock.json ├── package.json └── test │ ├── .babelrc.js │ ├── cases │ ├── test1-config │ ├── test1-input │ ├── test1-output │ ├── test2-config │ ├── test2-input │ └── test2-output │ ├── codepush.config.js │ └── plugin.test.js ├── babel.config.js ├── cli ├── commands │ ├── bundleCommand │ │ ├── bundleCodePush.js │ │ └── index.js │ ├── createHistoryCommand │ │ ├── createReleaseHistory.js │ │ └── index.js │ ├── releaseCommand │ │ ├── addToReleaseHistory.js │ │ ├── index.js │ │ └── release.js │ ├── showHistoryCommand │ │ └── index.js │ └── updateHistoryCommand │ │ ├── index.js │ │ └── updateReleaseHistory.js ├── constant.js ├── functions │ ├── getReactTempDir.js │ ├── makeCodePushBundle.js │ ├── prepareToBundleJS.js │ ├── runHermesEmitBinaryCommand.js │ └── runReactNativeBundleCommand.js ├── index.js └── utils │ ├── file-utils.js │ ├── fsUtils.js │ ├── hash-utils.js │ ├── promisfied-fs.js │ ├── showLogo.js │ └── zip.js ├── code-push-plugin-testing-framework ├── package.json ├── script │ ├── index.js │ ├── platform.js │ ├── projectManager.js │ ├── serverUtil.js │ ├── test.js │ ├── testBuilder.js │ ├── testConfig.js │ └── testUtil.js └── typings │ └── code-push-plugin-testing-framework.d.ts ├── docs ├── api-android.md ├── api-ios.md ├── api-js.md ├── multi-deployment-testing-android.md └── multi-deployment-testing-ios.md ├── eslint.config.mjs ├── ios ├── CodePush.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── CodePush │ ├── Base64 │ ├── Base64 │ │ ├── MF_Base64Additions.h │ │ └── MF_Base64Additions.m │ └── README.md │ ├── CodePush.h │ ├── CodePush.m │ ├── CodePushConfig.m │ ├── CodePushDownloadHandler.m │ ├── CodePushErrorUtils.m │ ├── CodePushPackage.m │ ├── CodePushTelemetryManager.m │ ├── CodePushUpdateUtils.m │ ├── CodePushUtils.m │ ├── JWT │ ├── Core │ │ ├── Algorithms │ │ │ ├── Base │ │ │ │ ├── JWTAlgorithm.h │ │ │ │ ├── JWTAlgorithmFactory.h │ │ │ │ ├── JWTAlgorithmFactory.m │ │ │ │ ├── JWTAlgorithmNone.h │ │ │ │ └── JWTAlgorithmNone.m │ │ │ ├── ESFamily │ │ │ │ ├── JWTAlgorithmESBase.h │ │ │ │ └── JWTAlgorithmESBase.m │ │ │ ├── HSFamily │ │ │ │ ├── JWTAlgorithmHSBase.h │ │ │ │ └── JWTAlgorithmHSBase.m │ │ │ ├── Holders │ │ │ │ ├── JWTAlgorithmDataHolder.h │ │ │ │ ├── JWTAlgorithmDataHolder.m │ │ │ │ ├── JWTAlgorithmDataHolderChain.h │ │ │ │ └── JWTAlgorithmDataHolderChain.m │ │ │ └── RSFamily │ │ │ │ ├── JWTAlgorithmRSBase.h │ │ │ │ ├── JWTAlgorithmRSBase.m │ │ │ │ ├── JWTRSAlgorithm.h │ │ │ │ └── RSKeys │ │ │ │ ├── JWTCryptoKey.h │ │ │ │ ├── JWTCryptoKey.m │ │ │ │ ├── JWTCryptoKeyExtractor.h │ │ │ │ ├── JWTCryptoKeyExtractor.m │ │ │ │ ├── JWTCryptoSecurity.h │ │ │ │ └── JWTCryptoSecurity.m │ │ ├── ClaimSet │ │ │ ├── JWTClaim.h │ │ │ ├── JWTClaim.m │ │ │ ├── JWTClaimsSet.h │ │ │ ├── JWTClaimsSet.m │ │ │ ├── JWTClaimsSetSerializer.h │ │ │ ├── JWTClaimsSetSerializer.m │ │ │ ├── JWTClaimsSetVerifier.h │ │ │ └── JWTClaimsSetVerifier.m │ │ ├── Coding │ │ │ ├── JWTCoding+ResultTypes.h │ │ │ ├── JWTCoding+ResultTypes.m │ │ │ ├── JWTCoding+VersionOne.h │ │ │ ├── JWTCoding+VersionOne.m │ │ │ ├── JWTCoding+VersionThree.h │ │ │ ├── JWTCoding+VersionThree.m │ │ │ ├── JWTCoding+VersionTwo.h │ │ │ ├── JWTCoding+VersionTwo.m │ │ │ ├── JWTCoding.h │ │ │ └── JWTCoding.m │ │ ├── FrameworkSupplement │ │ │ ├── JWT.h │ │ │ └── Map.modulemap │ │ └── Supplement │ │ │ ├── JWTBase64Coder.h │ │ │ ├── JWTBase64Coder.m │ │ │ ├── JWTDeprecations.h │ │ │ ├── JWTErrorDescription.h │ │ │ └── JWTErrorDescription.m │ ├── LICENSE │ └── README.md │ ├── RCTConvert+CodePushInstallMode.m │ ├── RCTConvert+CodePushUpdateState.m │ └── SSZipArchive │ ├── Common.h │ ├── README.md │ ├── SSZipArchive.h │ ├── SSZipArchive.m │ ├── aes │ ├── aes.h │ ├── aes_via_ace.h │ ├── aescrypt.c │ ├── aeskey.c │ ├── aesopt.h │ ├── aestab.c │ ├── aestab.h │ ├── brg_endian.h │ ├── brg_types.h │ ├── entropy.c │ ├── entropy.h │ ├── fileenc.c │ ├── fileenc.h │ ├── hmac.c │ ├── hmac.h │ ├── prng.c │ ├── prng.h │ ├── pwd2key.c │ ├── pwd2key.h │ ├── sha1.c │ └── sha1.h │ └── minizip │ ├── crypt.h │ ├── ioapi.c │ ├── ioapi.h │ ├── mztools.c │ ├── mztools.h │ ├── unzip.c │ ├── unzip.h │ ├── zip.c │ └── zip.h ├── logging.js ├── package-lock.json ├── package-mixins.js ├── package.json ├── react-native.config.js ├── scripts ├── generateBundledResourcesHash.js ├── getFilesInFolder.js └── recordFilesBeforeBundleCommand.js ├── test ├── template │ ├── android │ │ └── app │ │ │ └── src │ │ │ └── main │ │ │ ├── java │ │ │ └── com │ │ │ │ └── testcodepush │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ ├── codePushWrapper.js │ ├── index.js │ ├── ios │ │ └── TestCodePush │ │ │ └── AppDelegate.mm │ └── scenarios │ │ ├── scenarioCheckForUpdate.js │ │ ├── scenarioCheckForUpdateCustomKey.js │ │ ├── scenarioDisallowRestartImmediate.js │ │ ├── scenarioDisallowRestartOnResume.js │ │ ├── scenarioDisallowRestartOnSuspend.js │ │ ├── scenarioDownloadUpdate.js │ │ ├── scenarioInstall.js │ │ ├── scenarioInstallOnRestartWithRevert.js │ │ ├── scenarioInstallOnResumeWithRevert.js │ │ ├── scenarioInstallOnSuspendWithRevert.js │ │ ├── scenarioInstallRestart2x.js │ │ ├── scenarioInstallWithRevert.js │ │ ├── scenarioRestart.js │ │ ├── scenarioRestart2x.js │ │ ├── scenarioSync.js │ │ ├── scenarioSync2x.js │ │ ├── scenarioSyncMandatoryDefault.js │ │ ├── scenarioSyncMandatoryRestart.js │ │ ├── scenarioSyncMandatoryResume.js │ │ ├── scenarioSyncMandatorySuspend.js │ │ ├── scenarioSyncRestartDelay.js │ │ ├── scenarioSyncResume.js │ │ ├── scenarioSyncResumeDelay.js │ │ ├── scenarioSyncSuspend.js │ │ ├── scenarioSyncSuspendDelay.js │ │ ├── updateDeviceReady.js │ │ ├── updateNARConditional.js │ │ ├── updateNotifyApplicationReady.js │ │ ├── updateSync.js │ │ └── updateSync2x.js └── test.ts ├── tsconfig.json ├── tslint.json ├── typings └── react-native-code-push.d.ts └── versioning ├── BaseVersioning.js ├── BaseVersioning.test.js ├── IncrementalVersioning.js ├── IncrementalVersioning.test.js ├── SemverVersioning.js ├── SemverVersioning.test.js └── index.js /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @microsoft/appcenter-fte 2 | -------------------------------------------------------------------------------- /.github/archive/react-native-code-push-ci.yml: -------------------------------------------------------------------------------- 1 | name: React-native-code-push CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | test-android: 10 | name: Test Android app 11 | runs-on: macos-latest 12 | strategy: 13 | matrix: 14 | api-level: [ 27 ] 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v3 18 | - name: Start adb server 19 | run: adb devices 20 | - name: Gradle cache 21 | uses: gradle/gradle-build-action@v2 22 | - name: Download system image "android-${{ matrix.api-level }}" 23 | run: $ANDROID_HOME/tools/bin/sdkmanager "system-images;android-${{ matrix.api-level }};google_apis;x86" 24 | - name: Create android emulator 25 | run: $ANDROID_HOME/tools/bin/avdmanager create avd --force --name TestEmulator --abi google_apis/x86 --package 'system-images;android-${{ matrix.api-level }};google_apis;x86' --device "Nexus 6P" 26 | - name: Start android emulator 27 | run: $ANDROID_HOME/emulator/emulator -avd TestEmulator -noaudio -no-window -no-snapshot-save -no-boot-anim -memory 6144 & 28 | - name: Wait for emulator to boot 29 | run: $ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done' 30 | - run: adb shell settings put global window_animation_scale 0.0 31 | - run: adb shell settings put global transition_animation_scale 0.0 32 | - run: adb shell settings put global animator_duration_scale 0.0 33 | - name: Setup Java 34 | uses: actions/setup-java@v3 35 | with: 36 | distribution: 'microsoft' 37 | java-version: '11' 38 | - name: Setup Ruby 39 | uses: ruby/setup-ruby@v1 40 | with: 41 | ruby-version: '2.7.6' 42 | bundler-cache: true 43 | - name: Package Installation 44 | run: npm install 45 | - name: Setup Android tests 46 | run: npm run build:tests && npm run test:setup:android 47 | - name: Run Android test 48 | run: npm run test:fast:android 49 | 50 | test-iOS: 51 | name: Test iOS app 52 | runs-on: macos-latest 53 | env: 54 | NO_FLIPPER: ${{ secrets.NO_FLIPPER }} 55 | steps: 56 | - name: Checkout 57 | uses: actions/checkout@v2 58 | - name: Setup Ruby 59 | uses: ruby/setup-ruby@v1 60 | with: 61 | ruby-version: '2.7.6' 62 | bundler-cache: true 63 | - name: (Workaround) Install activesupport 7.0.8 64 | run: gem install activesupport -v 7.0.8 65 | - name: Install dependencies 66 | run: npm install 67 | - name: Setup iOS tests 68 | run: npm run build:tests && npm run test:setup:ios 69 | - name: Run tests 70 | run: npm run test:fast:ios 71 | -------------------------------------------------------------------------------- /.github/policies/resourceManagement.yml: -------------------------------------------------------------------------------- 1 | id: 2 | name: GitOps.PullRequestIssueManagement 3 | description: GitOps.PullRequestIssueManagement primitive 4 | owner: 5 | resource: repository 6 | disabled: false 7 | where: 8 | configuration: 9 | resourceManagementConfiguration: 10 | scheduledSearches: 11 | - description: 12 | frequencies: 13 | - hourly: 14 | hour: 4 15 | filters: 16 | - isOpen 17 | - isNotLabeledWith: 18 | label: bug 19 | - isNotLabeledWith: 20 | label: security 21 | - isNotLabeledWith: 22 | label: stale 23 | - isNotLabeledWith: 24 | label: do not close 25 | - noActivitySince: 26 | days: 60 27 | - isIssue 28 | - isNotAssigned 29 | actions: 30 | - addLabel: 31 | label: stale 32 | - addReply: 33 | reply: This issue has been automatically marked as stale because it has not had any activity for 60 days. It will be closed if no further activity occurs within 15 days of this comment. 34 | - description: 35 | frequencies: 36 | - hourly: 37 | hour: 6 38 | filters: 39 | - isOpen 40 | - isIssue 41 | - hasLabel: 42 | label: stale 43 | - isNotLabeledWith: 44 | label: bug 45 | - isNotLabeledWith: 46 | label: do not close 47 | - isNotAssigned 48 | - noActivitySince: 49 | days: 15 50 | actions: 51 | - addReply: 52 | reply: This issue will now be closed because it hasn't had any activity for 15 days after stale. Please feel free to open a new issue if you still have a question/issue or suggestion. 53 | - closeIssue 54 | eventResponderTasks: 55 | - if: 56 | - payloadType: Issue_Comment 57 | - hasLabel: 58 | label: stale 59 | then: 60 | - removeLabel: 61 | label: stale 62 | description: 63 | onFailure: 64 | onSuccess: 65 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # node.js 25 | # 26 | node_modules/ 27 | npm-debug.log 28 | 29 | # Don't publish example apps 30 | Examples/ 31 | Recipes/ 32 | 33 | # Don't publish testing code 34 | bin/ 35 | test/ 36 | 37 | # Remove after this framework is published on NPM 38 | code-push-plugin-testing-framework/ 39 | 40 | # Android build artifacts and Android Studio bits 41 | android/app/build 42 | android/local.properties 43 | android/.gradle 44 | android/**/*.iml 45 | android/.idea 46 | 47 | 48 | # Windows 49 | windows/.vs/ 50 | windows/obj/ 51 | windows 52 | windows-legacy 53 | 54 | #Tests 55 | windows/CodePush.Net46.Test 56 | 57 | #Visual Studio files 58 | *.[Oo]bj 59 | *.user 60 | *.aps 61 | *.pch 62 | *.vspscc 63 | *.vssscc 64 | *_i.c 65 | *_p.c 66 | *.ncb 67 | *.suo 68 | *.tlb 69 | *.tlh 70 | *.bak 71 | *.[Cc]ache 72 | *.ilk 73 | *.log 74 | *.lib 75 | *.sbr 76 | *.sdf 77 | *.opensdf 78 | *.opendb 79 | *.unsuccessfulbuild 80 | ipch/ 81 | [Oo]bj/ 82 | [Bb]in 83 | [Dd]ebug*/ 84 | [Rr]elease*/ 85 | Ankh.NoLoad 86 | 87 | #NuGet 88 | packages/ 89 | *.nupkg 90 | 91 | # VSCode 92 | .vscode/ 93 | 94 | # IntelliJIDEA 95 | .idea/ 96 | 97 | 98 | # Github 99 | .github/ 100 | 101 | # Git 102 | .git/ 103 | 104 | .watchmanconfig 105 | 106 | # prevent CLI code ignored (ignored by "[Rr]elease*/") 107 | !cli/commands/releaseCommand/ 108 | 109 | # currently not used 110 | babel-plugin-code-push/ 111 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Tests-android", 6 | "type": "node", 7 | "request": "launch", 8 | "preLaunchTask": "Setup-android", 9 | "runtimeExecutable": "npm", 10 | "runtimeArgs": [ 11 | "run", 12 | "test:debugger:android" 13 | ], 14 | "port": 9229, 15 | "stopOnEntry": false, 16 | "sourceMaps": true, 17 | "console": "internalConsole", 18 | "internalConsoleOptions": "openOnSessionStart", 19 | "autoAttachChildProcesses": true, 20 | "timeout": 100000 21 | }, 22 | { 23 | "name": "Tests-ios", 24 | "type": "node", 25 | "request": "launch", 26 | "preLaunchTask": "Setup-ios", 27 | "runtimeExecutable": "npm", 28 | "runtimeArgs": [ 29 | "run", 30 | "test:debugger:ios" 31 | ], 32 | "port": 9229, 33 | "stopOnEntry": false, 34 | "sourceMaps": true, 35 | "console": "internalConsole", 36 | "internalConsoleOptions": "openOnSessionStart", 37 | "autoAttachChildProcesses": true, 38 | "timeout": 100000 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "shell", 6 | "label": "Build", 7 | "command": "npm", 8 | "args": [ 9 | "run", 10 | "build:tests" 11 | ], 12 | "presentation": { 13 | "echo": false, 14 | "focus": false 15 | }, 16 | "problemMatcher": [ 17 | "$tsc" 18 | ] 19 | }, 20 | { 21 | "type": "shell", 22 | "label": "Setup-android", 23 | "dependsOn": "Build", 24 | "command": "npm", 25 | "args": [ 26 | "run", 27 | "test:setup:android" 28 | ], 29 | "presentation": { 30 | "echo": false, 31 | "focus": false 32 | }, 33 | "problemMatcher": [ 34 | "$tsc" 35 | ] 36 | }, 37 | { 38 | "type": "shell", 39 | "label": "Setup-ios", 40 | "dependsOn": "Build", 41 | "command": "npm", 42 | "args": [ 43 | "run", 44 | "test:setup:ios" 45 | ], 46 | "presentation": { 47 | "echo": false, 48 | "focus": false 49 | }, 50 | "problemMatcher": [ 51 | "$tsc" 52 | ] 53 | } 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /AlertAdapter.js: -------------------------------------------------------------------------------- 1 | import React, { Platform } from "react-native"; 2 | let { Alert } = React; 3 | 4 | if (Platform.OS === "android") { 5 | const { NativeModules: { CodePushDialog } } = React; 6 | 7 | Alert = { 8 | alert(title, message, buttons) { 9 | if (buttons.length > 2) { 10 | throw "Can only show 2 buttons for Android dialog."; 11 | } 12 | 13 | const button1Text = buttons[0] ? buttons[0].text : null, 14 | button2Text = buttons[1] ? buttons[1].text : null; 15 | 16 | CodePushDialog.showDialog( 17 | title, message, button1Text, button2Text, 18 | (buttonId) => { buttons[buttonId].onPress && buttons[buttonId].onPress(); }, 19 | (error) => { throw error; }); 20 | } 21 | }; 22 | } 23 | 24 | module.exports = { Alert }; -------------------------------------------------------------------------------- /CodePush.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = 'CodePush' 7 | s.version = package['version'].gsub(/v|-beta/, '') 8 | s.summary = package['description'] 9 | s.author = package['author'] 10 | s.license = package['license'] 11 | s.homepage = package['homepage'] 12 | s.source = { :git => 'https://github.com/Soomgo-Mobile/react-native-code-push.git', :tag => "v#{s.version}"} 13 | s.ios.deployment_target = '9.0' 14 | s.tvos.deployment_target = '9.0' 15 | s.preserve_paths = '*.js' 16 | s.library = 'z' 17 | s.source_files = 'ios/CodePush/*.{h,m}' 18 | s.public_header_files = ['ios/CodePush/CodePush.h'] 19 | 20 | # Note: Even though there are copy/pasted versions of some of these dependencies in the repo, 21 | # we explicitly let CocoaPods pull in the versions below so all dependencies are resolved and 22 | # linked properly at a parent workspace level. 23 | s.dependency 'React-Core' 24 | s.dependency 'SSZipArchive', '~> 2.2.2' 25 | s.dependency 'JWT', '~> 3.0.0-beta.12' 26 | s.dependency 'Base64', '~> 1.1' 27 | end 28 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native', 4 | }; 5 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | **/.xcode.env.local 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | .cxx/ 34 | *.keystore 35 | !debug.keystore 36 | 37 | # node.js 38 | # 39 | node_modules/ 40 | npm-debug.log 41 | yarn-error.log 42 | 43 | # fastlane 44 | # 45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 46 | # screenshots whenever they are needed. 47 | # For more information about the recommended setup visit: 48 | # https://docs.fastlane.tools/best-practices/source-control/ 49 | 50 | **/fastlane/report.xml 51 | **/fastlane/Preview.html 52 | **/fastlane/screenshots 53 | **/fastlane/test_output 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # Ruby / CocoaPods 59 | **/Pods/ 60 | /vendor/bundle/ 61 | 62 | # Temporary files created by Metro to check the health of the file watcher 63 | .metro-health-check* 64 | 65 | # testing 66 | /coverage 67 | 68 | # Yarn 69 | .yarn/* 70 | !.yarn/patches 71 | !.yarn/plugins 72 | !.yarn/releases 73 | !.yarn/sdks 74 | !.yarn/versions 75 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: "avoid", 3 | bracketSameLine: true, 4 | bracketSpacing: false, 5 | singleQuote: false, 6 | trailingComma: "all", 7 | }; 8 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version 4 | ruby ">= 2.6.10" 5 | 6 | # Exclude problematic versions of cocoapods and activesupport that causes build failures. 7 | gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1' 8 | gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0' 9 | gem 'xcodeproj', '< 1.26.0' 10 | gem 'concurrent-ruby', '< 1.3.4' 11 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.7) 5 | base64 6 | nkf 7 | rexml 8 | activesupport (6.1.7.10) 9 | concurrent-ruby (~> 1.0, >= 1.0.2) 10 | i18n (>= 1.6, < 2) 11 | minitest (>= 5.1) 12 | tzinfo (~> 2.0) 13 | zeitwerk (~> 2.3) 14 | addressable (2.8.7) 15 | public_suffix (>= 2.0.2, < 7.0) 16 | algoliasearch (1.27.5) 17 | httpclient (~> 2.8, >= 2.8.3) 18 | json (>= 1.5.1) 19 | atomos (0.1.3) 20 | base64 (0.2.0) 21 | claide (1.1.0) 22 | cocoapods (1.15.2) 23 | addressable (~> 2.8) 24 | claide (>= 1.0.2, < 2.0) 25 | cocoapods-core (= 1.15.2) 26 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 27 | cocoapods-downloader (>= 2.1, < 3.0) 28 | cocoapods-plugins (>= 1.0.0, < 2.0) 29 | cocoapods-search (>= 1.0.0, < 2.0) 30 | cocoapods-trunk (>= 1.6.0, < 2.0) 31 | cocoapods-try (>= 1.1.0, < 2.0) 32 | colored2 (~> 3.1) 33 | escape (~> 0.0.4) 34 | fourflusher (>= 2.3.0, < 3.0) 35 | gh_inspector (~> 1.0) 36 | molinillo (~> 0.8.0) 37 | nap (~> 1.0) 38 | ruby-macho (>= 2.3.0, < 3.0) 39 | xcodeproj (>= 1.23.0, < 2.0) 40 | cocoapods-core (1.15.2) 41 | activesupport (>= 5.0, < 8) 42 | addressable (~> 2.8) 43 | algoliasearch (~> 1.0) 44 | concurrent-ruby (~> 1.1) 45 | fuzzy_match (~> 2.0.4) 46 | nap (~> 1.0) 47 | netrc (~> 0.11) 48 | public_suffix (~> 4.0) 49 | typhoeus (~> 1.0) 50 | cocoapods-deintegrate (1.0.5) 51 | cocoapods-downloader (2.1) 52 | cocoapods-plugins (1.0.0) 53 | nap 54 | cocoapods-search (1.0.1) 55 | cocoapods-trunk (1.6.0) 56 | nap (>= 0.8, < 2.0) 57 | netrc (~> 0.11) 58 | cocoapods-try (1.2.0) 59 | colored2 (3.1.2) 60 | concurrent-ruby (1.3.3) 61 | escape (0.0.4) 62 | ethon (0.16.0) 63 | ffi (>= 1.15.0) 64 | ffi (1.17.1) 65 | fourflusher (2.3.1) 66 | fuzzy_match (2.0.4) 67 | gh_inspector (1.1.3) 68 | httpclient (2.8.3) 69 | i18n (1.14.7) 70 | concurrent-ruby (~> 1.0) 71 | json (2.7.6) 72 | minitest (5.25.4) 73 | molinillo (0.8.0) 74 | nanaimo (0.3.0) 75 | nap (1.1.0) 76 | netrc (0.11.0) 77 | nkf (0.2.0) 78 | public_suffix (4.0.7) 79 | rexml (3.4.1) 80 | ruby-macho (2.5.1) 81 | typhoeus (1.4.1) 82 | ethon (>= 0.9.0) 83 | tzinfo (2.0.6) 84 | concurrent-ruby (~> 1.0) 85 | xcodeproj (1.25.1) 86 | CFPropertyList (>= 2.3.3, < 4.0) 87 | atomos (~> 0.1.3) 88 | claide (>= 1.0.2, < 2.0) 89 | colored2 (~> 3.1) 90 | nanaimo (~> 0.3.0) 91 | rexml (>= 3.3.6, < 4.0) 92 | zeitwerk (2.6.18) 93 | 94 | PLATFORMS 95 | ruby 96 | 97 | DEPENDENCIES 98 | activesupport (>= 6.1.7.5, != 7.1.0) 99 | cocoapods (>= 1.13, != 1.15.1, != 1.15.0) 100 | concurrent-ruby (< 1.3.4) 101 | xcodeproj (< 1.26.0) 102 | 103 | RUBY VERSION 104 | ruby 2.6.10p210 105 | 106 | BUNDLED WITH 107 | 1.17.2 108 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import React from 'react'; 6 | import ReactTestRenderer from 'react-test-renderer'; 7 | import App from '../App'; 8 | 9 | test('renders correctly', async () => { 10 | await ReactTestRenderer.act(() => { 11 | ReactTestRenderer.create(); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/debug.keystore -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/java/com/codepushdemoapp/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.codepushdemoapp 2 | 3 | import com.facebook.react.ReactActivity 4 | import com.facebook.react.ReactActivityDelegate 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate 7 | 8 | class MainActivity : ReactActivity() { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | override fun getMainComponentName(): String = "CodePushDemoApp" 15 | 16 | /** 17 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 18 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 19 | */ 20 | override fun createReactActivityDelegate(): ReactActivityDelegate = 21 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 22 | } 23 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/java/com/codepushdemoapp/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.codepushdemoapp 2 | 3 | import android.app.Application 4 | import com.facebook.react.PackageList 5 | import com.facebook.react.ReactApplication 6 | import com.facebook.react.ReactHost 7 | import com.facebook.react.ReactNativeHost 8 | import com.facebook.react.ReactPackage 9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load 10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost 11 | import com.facebook.react.defaults.DefaultReactNativeHost 12 | import com.facebook.react.soloader.OpenSourceMergedSoMapping 13 | import com.facebook.soloader.SoLoader 14 | import com.microsoft.codepush.react.CodePush 15 | 16 | class MainApplication : Application(), ReactApplication { 17 | 18 | override val reactNativeHost: ReactNativeHost = 19 | object : DefaultReactNativeHost(this) { 20 | override fun getPackages(): List = 21 | PackageList(this).packages.apply { 22 | // Packages that cannot be autolinked yet can be added manually here, for example: 23 | // add(MyReactNativePackage()) 24 | } 25 | 26 | override fun getJSMainModuleName(): String = "index" 27 | 28 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG 29 | 30 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 31 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED 32 | 33 | override fun getJSBundleFile(): String { 34 | return CodePush.getJSBundleFile() 35 | } 36 | } 37 | 38 | override val reactHost: ReactHost 39 | get() = getDefaultReactHost(applicationContext, reactNativeHost) 40 | 41 | override fun onCreate() { 42 | super.onCreate() 43 | SoLoader.init(this, OpenSourceMergedSoMapping) 44 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 45 | // If you opted-in for the New Architecture, we load the native entry point for this app. 46 | load() 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 22 | 23 | 24 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CodePushDemoApp 3 | 4 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | buildToolsVersion = "35.0.0" 4 | minSdkVersion = 24 5 | compileSdkVersion = 35 6 | targetSdkVersion = 34 7 | ndkVersion = "27.1.12297006" 8 | kotlinVersion = "2.0.21" 9 | } 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | dependencies { 15 | classpath("com.android.tools.build:gradle") 16 | classpath("com.facebook.react:react-native-gradle-plugin") 17 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") 18 | } 19 | } 20 | 21 | apply plugin: "com.facebook.react.rootproject" 22 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m 13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | 25 | # Use this property to specify which architecture you want to build. 26 | # You can also override it from the CLI using 27 | # ./gradlew -PreactNativeArchitectures=x86_64 28 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 29 | 30 | # Use this property to enable support to the new architecture. 31 | # This will allow you to use TurboModules and the Fabric render in 32 | # your application. You should enable this flag either if you want 33 | # to write custom TurboModules/Fabric components OR use libraries that 34 | # are providing them. 35 | newArchEnabled=false 36 | 37 | # Use this property to enable or disable the Hermes JS engine. 38 | # If set to false, you will be using JSC instead. 39 | hermesEnabled=true 40 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/Examples/CodePushDemoApp/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } 2 | plugins { id("com.facebook.react.settings") } 3 | extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } 4 | rootProject.name = 'CodePushDemoApp' 5 | include ':app' 6 | includeBuild('../node_modules/@react-native/gradle-plugin') 7 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CodePushDemoApp", 3 | "displayName": "CodePushDemoApp" 4 | } 5 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/code-push.config.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { 3 | CliConfigInterface, 4 | ReleaseHistoryInterface, 5 | } from "@bravemobile/react-native-code-push"; 6 | import {invalidateCloudfrontCache} from "./scripts/invalidateCloudfrontCache"; 7 | import {uploadFileToS3} from "./scripts/uploadFileToS3"; 8 | 9 | export const CDN_HOST = "https://your.cdn.provider.com"; 10 | 11 | function historyJsonFileRemotePath( 12 | platform: "ios" | "android", 13 | identifier: string, 14 | binaryVersion: string, 15 | ) { 16 | return `histories/${platform}/${identifier}/${binaryVersion}.json`; 17 | } 18 | 19 | function bundleFileRemotePath( 20 | platform: "ios" | "android", 21 | identifier: string, 22 | fileName: string, 23 | ) { 24 | return `bundles/${platform}/${identifier}/${fileName}`; 25 | } 26 | 27 | const Config: CliConfigInterface = { 28 | bundleUploader: async ( 29 | source: string, 30 | platform: "ios" | "android", 31 | identifier = "staging", 32 | ): Promise<{downloadUrl: string}> => { 33 | const fileName = source.split("/").pop(); 34 | const remoteBundlePath = bundleFileRemotePath( 35 | platform, 36 | identifier, 37 | fileName!, 38 | ); 39 | 40 | await uploadFileToS3({ 41 | pathToLocalFile: source, 42 | key: remoteBundlePath, 43 | }); 44 | 45 | const downloadUrl = `${CDN_HOST}/${remoteBundlePath}`; 46 | 47 | console.log("🎉 Bundle File uploaded:", downloadUrl); 48 | 49 | return { 50 | downloadUrl: downloadUrl, 51 | }; 52 | }, 53 | 54 | getReleaseHistory: async ( 55 | targetBinaryVersion: string, 56 | platform: "ios" | "android", 57 | identifier = "staging", 58 | ): Promise => { 59 | const remoteJsonPath = historyJsonFileRemotePath( 60 | platform, 61 | identifier, 62 | targetBinaryVersion, 63 | ); 64 | 65 | const jsonUrl = `${CDN_HOST}/${remoteJsonPath}`; 66 | 67 | try { 68 | const {data} = await axios.get(jsonUrl); 69 | return data as ReleaseHistoryInterface; 70 | } catch (error) { 71 | if ( 72 | axios.isAxiosError(error) && 73 | error.response != null && 74 | [403, 404].includes(error.response.status) 75 | ) { 76 | console.error("Release history file not found at", jsonUrl); 77 | } 78 | throw error; 79 | } 80 | }, 81 | 82 | setReleaseHistory: async ( 83 | targetBinaryVersion: string, 84 | jsonFilePath: string, 85 | releaseInfo: ReleaseHistoryInterface, 86 | platform: "ios" | "android", 87 | identifier = "staging", 88 | ): Promise => { 89 | // upload JSON file or call API using `releaseInfo` metadata. 90 | 91 | const remoteJsonPath = historyJsonFileRemotePath( 92 | platform, 93 | identifier, 94 | targetBinaryVersion, 95 | ); 96 | 97 | await uploadFileToS3({ 98 | pathToLocalFile: jsonFilePath, 99 | key: remoteJsonPath, 100 | }); 101 | 102 | await invalidateCloudfrontCache({ 103 | key: remoteJsonPath, 104 | }); 105 | 106 | const jsonUrl = `${CDN_HOST}/${remoteJsonPath}`; 107 | 108 | console.log("🎉 Release history File uploaded:", jsonUrl); 109 | }, 110 | }; 111 | 112 | module.exports = Config; 113 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/CodePushDemoApp-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | #import 6 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/CodePushDemoApp.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/CodePushDemoApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import React 3 | import React_RCTAppDelegate 4 | import ReactAppDependencyProvider 5 | 6 | @main 7 | class AppDelegate: RCTAppDelegate { 8 | override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { 9 | self.moduleName = "CodePushDemoApp" 10 | self.dependencyProvider = RCTAppDependencyProvider() 11 | 12 | // You can add your custom initial props in the dictionary below. 13 | // They will be passed down to the ViewController used by React Native. 14 | self.initialProps = [:] 15 | 16 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 17 | } 18 | 19 | override func sourceURL(for bridge: RCTBridge) -> URL? { 20 | self.bundleURL() 21 | } 22 | 23 | override func bundleURL() -> URL? { 24 | #if DEBUG 25 | RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") 26 | #else 27 | // Bundle.main.url(forResource: "main", withExtension: "jsbundle") 28 | CodePush.bundleURL() 29 | #endif 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/CodePushDemoApp/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/CodePushDemoApp/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/CodePushDemoApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | CodePushDemoApp 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(CURRENT_PROJECT_VERSION) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | 30 | NSAllowsArbitraryLoads 31 | 32 | NSAllowsLocalNetworking 33 | 34 | 35 | NSLocationWhenInUseUsageDescription 36 | 37 | UILaunchStoryboardName 38 | LaunchScreen 39 | UIRequiredDeviceCapabilities 40 | 41 | arm64 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UIViewControllerBasedStatusBarAppearance 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/CodePushDemoApp/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryUserDefaults 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | CA92.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategorySystemBootTime 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | 35F9.1 29 | 30 | 31 | 32 | NSPrivacyCollectedDataTypes 33 | 34 | NSPrivacyTracking 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Resolve react_native_pods.rb with node to allow for hoisting 2 | require Pod::Executable.execute_command('node', ['-p', 3 | 'require.resolve( 4 | "react-native/scripts/react_native_pods.rb", 5 | {paths: [process.argv[1]]}, 6 | )', __dir__]).strip 7 | 8 | ENV['RCT_NEW_ARCH_ENABLED'] = '0' 9 | 10 | platform :ios, min_ios_version_supported 11 | prepare_react_native_project! 12 | 13 | linkage = ENV['USE_FRAMEWORKS'] 14 | if linkage != nil 15 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 16 | use_frameworks! :linkage => linkage.to_sym 17 | end 18 | 19 | target 'CodePushDemoApp' do 20 | config = use_native_modules! 21 | 22 | use_react_native!( 23 | :path => config[:reactNativePath], 24 | # An absolute path to your application root. 25 | :app_path => "#{Pod::Config.instance.installation_root}/.." 26 | ) 27 | 28 | post_install do |installer| 29 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 30 | react_native_post_install( 31 | installer, 32 | config[:reactNativePath], 33 | :mac_catalyst_enabled => false, 34 | # :ccache_enabled => true 35 | ) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | }; 4 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/metro.config.js: -------------------------------------------------------------------------------- 1 | const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config'); 2 | 3 | /** 4 | * Metro configuration 5 | * https://reactnative.dev/docs/metro 6 | * 7 | * @type {import('@react-native/metro-config').MetroConfig} 8 | */ 9 | const config = {}; 10 | 11 | module.exports = mergeConfig(getDefaultConfig(__dirname), config); 12 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CodePushDemoApp", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "lint": "eslint .", 9 | "start": "react-native start", 10 | "test": "jest" 11 | }, 12 | "dependencies": { 13 | "@bravemobile/react-native-code-push": "^9.0.0", 14 | "axios": "^1.7.9", 15 | "react": "18.3.1", 16 | "react-native": "0.77.1" 17 | }, 18 | "devDependencies": { 19 | "@aws-sdk/client-cloudfront": "^3.750.0", 20 | "@aws-sdk/client-s3": "^3.750.0", 21 | "@aws-sdk/lib-storage": "^3.750.0", 22 | "@babel/core": "^7.25.2", 23 | "@babel/preset-env": "^7.25.3", 24 | "@babel/runtime": "^7.25.0", 25 | "@react-native-community/cli": "15.0.1", 26 | "@react-native-community/cli-platform-android": "15.0.1", 27 | "@react-native-community/cli-platform-ios": "15.0.1", 28 | "@react-native/babel-preset": "0.77.1", 29 | "@react-native/eslint-config": "0.77.1", 30 | "@react-native/metro-config": "0.77.1", 31 | "@react-native/typescript-config": "0.77.1", 32 | "@types/jest": "^29.5.13", 33 | "@types/react": "^18.2.6", 34 | "@types/react-test-renderer": "^18.0.0", 35 | "eslint": "^8.19.0", 36 | "jest": "^29.6.3", 37 | "prettier": "2.8.8", 38 | "react-test-renderer": "18.3.1", 39 | "typescript": "5.0.4" 40 | }, 41 | "engines": { 42 | "node": ">=18" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/scripts/invalidateCloudfrontCache.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CloudFrontClient, 3 | CreateInvalidationCommand, 4 | } from "@aws-sdk/client-cloudfront"; 5 | 6 | const CLOUDFRONT_DISTRIBUTION_ID = "DISTRIBUTION_ID"; 7 | 8 | const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID; 9 | const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY; 10 | 11 | export async function invalidateCloudfrontCache({key}: {key: string}) { 12 | console.log(`log: Start creating cache invalidation (${key})`); 13 | const cloudfront = new CloudFrontClient({ 14 | region: "ap-northeast-2", 15 | credentials: { 16 | accessKeyId: AWS_ACCESS_KEY_ID!, 17 | secretAccessKey: AWS_SECRET_ACCESS_KEY!, 18 | }, 19 | }); 20 | 21 | const command = new CreateInvalidationCommand({ 22 | DistributionId: CLOUDFRONT_DISTRIBUTION_ID, 23 | InvalidationBatch: { 24 | CallerReference: `${Date.now()}`, 25 | Paths: { 26 | Quantity: 1, 27 | Items: [`/${key}`], 28 | }, 29 | }, 30 | }); 31 | 32 | try { 33 | const data = await cloudfront.send(command); 34 | console.log(`Cache invalidation created (ID: ${data.Invalidation?.Id})`); 35 | } catch (error) { 36 | console.error("Cache invalidation failed", error); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/scripts/uploadFileToS3.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import {S3Client} from "@aws-sdk/client-s3"; 3 | import {Upload} from "@aws-sdk/lib-storage"; 4 | 5 | const BUCKET_NAME = "code-push-bucket"; 6 | const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID; 7 | const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY; 8 | 9 | interface Params { 10 | pathToLocalFile: string; 11 | key: string; 12 | } 13 | 14 | export async function uploadFileToS3({pathToLocalFile, key}: Params) { 15 | const s3Client = new S3Client({ 16 | region: "ap-northeast-2", 17 | credentials: { 18 | accessKeyId: AWS_ACCESS_KEY_ID!, 19 | secretAccessKey: AWS_SECRET_ACCESS_KEY!, 20 | }, 21 | }); 22 | 23 | const fileStream = fs.createReadStream(pathToLocalFile); 24 | 25 | const uploader = new Upload({ 26 | client: s3Client, 27 | params: { 28 | Bucket: BUCKET_NAME, 29 | Key: key, 30 | Body: fileStream, 31 | ACL: "public-read", 32 | }, 33 | }); 34 | 35 | const {Location} = await uploader.done(); 36 | console.log(`log: 🎉 File uploaded successfully [${Location}]`); 37 | } 38 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@react-native/typescript-config/tsconfig.json", 3 | "ts-node": { 4 | "compilerOptions": { 5 | "module": "CommonJS", 6 | "types": ["node"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Examples/CodePushDemoApp/utils/index.ts: -------------------------------------------------------------------------------- 1 | import {Platform} from "react-native"; 2 | 3 | export function getPlatform() { 4 | switch (Platform.OS) { 5 | case "ios": 6 | return "ios"; 7 | case "android": 8 | return "android"; 9 | default: 10 | throw new Error("Unsupported platform"); 11 | } 12 | } 13 | 14 | export function trackError(error: unknown) { 15 | console.error(error); // fake implementation 16 | } 17 | 18 | export function findKeyByValue( 19 | object: Record, 20 | value: unknown, 21 | ) { 22 | return Object.keys(object).find(key => object[key] === value); 23 | } 24 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Microsoft CodePush Plugin for React Native 2 | 3 | Copyright (c) Microsoft Corporation 4 | 5 | All rights reserved. 6 | 7 | MIT License 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Recipes/UpdateButton.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pkg = require('./package'); 4 | var React = require('react-native'); 5 | var { 6 | AppRegistry, 7 | StyleSheet, 8 | Text, 9 | View, 10 | } = React; 11 | var Button = require('react-native-button'); 12 | 13 | var CodePush = require('react-native-code-push'); 14 | 15 | var UpdateButton = React.createClass({ 16 | getInitialState: function() { 17 | return {}; 18 | }, 19 | componentDidMount: function() { 20 | CodePush.checkForUpdate().done((update) => { 21 | if (update && !update.downloadURL) { 22 | this.setState({ 23 | update: update 24 | }); 25 | } 26 | }); 27 | }, 28 | update: function() { 29 | this.state.update.download().done((newPackage) => { 30 | newPackage.install(); 31 | }); 32 | }, 33 | render: function() { 34 | var updateButton = null; 35 | if (this.state.update) { 36 | updateButton = ; 37 | } 38 | 39 | return ( 40 | 41 | 42 | Welcome to {pkg.name} {pkg.version}! 43 | 44 | {updateButton} 45 | 46 | ); 47 | } 48 | }); 49 | 50 | var styles = StyleSheet.create({ 51 | container: { 52 | flex: 1, 53 | justifyContent: 'center', 54 | alignItems: 'center', 55 | backgroundColor: '#F5FCFF', 56 | } 57 | }); 58 | 59 | AppRegistry.registerComponent('UpdateButton', () => UpdateButton); 60 | -------------------------------------------------------------------------------- /Recipes/UpdateOnStart.ios.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var pkg = require('./package'); 4 | var React = require('react-native'); 5 | var { 6 | AppRegistry, 7 | StyleSheet, 8 | Text, 9 | View, 10 | } = React; 11 | 12 | var CodePush = require('react-native-code-push'); 13 | 14 | var UpdateOnStart = React.createClass({ 15 | componentDidMount: function() { 16 | CodePush.checkForUpdate().done((update) => { 17 | if (update && update.downloadUrl) { 18 | update.download().done((newPackage) => { 19 | newPackage.install(); 20 | }); 21 | } 22 | }); 23 | }, 24 | render: function() { 25 | return ( 26 | 27 | 28 | Welcome to {pkg.name} {pkg.version}! 29 | 30 | 31 | ); 32 | } 33 | }); 34 | 35 | var styles = StyleSheet.create({ 36 | container: { 37 | flex: 1, 38 | justifyContent: 'center', 39 | alignItems: 'center', 40 | backgroundColor: '#F5FCFF', 41 | } 42 | }); 43 | 44 | AppRegistry.registerComponent('UpdateOnStart', () => UpdateOnStart); 45 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.library" 2 | 3 | def DEFAULT_COMPILE_SDK_VERSION = 26 4 | def DEFAULT_BUILD_TOOLS_VERSION = "26.0.3" 5 | def DEFAULT_TARGET_SDK_VERSION = 26 6 | def DEFAULT_MIN_SDK_VERSION = 16 7 | 8 | android { 9 | compileSdkVersion rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION 10 | buildToolsVersion rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEFAULT_BUILD_TOOLS_VERSION 11 | 12 | defaultConfig { 13 | minSdkVersion rootProject.hasProperty('minSdkVersion') ? rootProject.minSdkVersion : DEFAULT_MIN_SDK_VERSION 14 | targetSdkVersion rootProject.hasProperty('targetSdkVersion') ? rootProject.targetSdkVersion : DEFAULT_TARGET_SDK_VERSION 15 | versionCode 1 16 | versionName "1.0" 17 | } 18 | 19 | lintOptions { 20 | abortOnError false 21 | } 22 | 23 | defaultConfig { 24 | consumerProguardFiles 'proguard-rules.pro' 25 | } 26 | } 27 | 28 | dependencies { 29 | implementation "com.facebook.react:react-native:+" 30 | implementation 'com.nimbusds:nimbus-jose-jwt:9.37.3' 31 | } 32 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Invoked via reflection, when setting js bundle. 20 | -keepclassmembers class com.facebook.react.ReactInstanceManager { 21 | private final ** mBundleLoader; 22 | } 23 | 24 | # Can't find referenced class org.bouncycastle.** 25 | -dontwarn com.nimbusds.jose.** 26 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushBuilder.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | import android.content.Context; 4 | 5 | public class CodePushBuilder { 6 | private String mDeploymentKey; 7 | private Context mContext; 8 | 9 | private boolean mIsDebugMode; 10 | private String mServerUrl; 11 | private Integer mPublicKeyResourceDescriptor; 12 | 13 | public CodePushBuilder(Context context) { 14 | this.mDeploymentKey = "deprecated_deployment_key"; 15 | this.mContext = context; 16 | this.mServerUrl = CodePush.getServiceUrl(); 17 | } 18 | 19 | public CodePushBuilder setIsDebugMode(boolean isDebugMode) { 20 | this.mIsDebugMode = isDebugMode; 21 | return this; 22 | } 23 | 24 | public CodePushBuilder setServerUrl(String serverUrl) { 25 | this.mServerUrl = serverUrl; 26 | return this; 27 | } 28 | 29 | public CodePushBuilder setPublicKeyResourceDescriptor(int publicKeyResourceDescriptor) { 30 | this.mPublicKeyResourceDescriptor = publicKeyResourceDescriptor; 31 | return this; 32 | } 33 | 34 | public CodePush build() { 35 | return new CodePush(this.mContext, this.mIsDebugMode, this.mServerUrl, this.mPublicKeyResourceDescriptor); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushConstants.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | public class CodePushConstants { 4 | public static final String ASSETS_BUNDLE_PREFIX = "assets://"; 5 | public static final String BINARY_MODIFIED_TIME_KEY = "binaryModifiedTime"; 6 | public static final String CODE_PUSH_FOLDER_PREFIX = "CodePush"; 7 | public static final String CODE_PUSH_HASH_FILE_NAME = "CodePushHash"; 8 | public static final String CODE_PUSH_OLD_HASH_FILE_NAME = "CodePushHash.json"; 9 | public static final String CODE_PUSH_PREFERENCES = "CodePush"; 10 | public static final String CURRENT_PACKAGE_KEY = "currentPackage"; 11 | public static final String DEFAULT_JS_BUNDLE_NAME = "index.android.bundle"; 12 | public static final String DIFF_MANIFEST_FILE_NAME = "hotcodepush.json"; 13 | public static final int DOWNLOAD_BUFFER_SIZE = 1024 * 256; 14 | public static final String DOWNLOAD_FILE_NAME = "download.zip"; 15 | public static final String DOWNLOAD_PROGRESS_EVENT_NAME = "CodePushDownloadProgress"; 16 | public static final String DOWNLOAD_URL_KEY = "downloadUrl"; 17 | public static final String FAILED_UPDATES_KEY = "CODE_PUSH_FAILED_UPDATES"; 18 | public static final String PACKAGE_FILE_NAME = "app.json"; 19 | public static final String PACKAGE_HASH_KEY = "packageHash"; 20 | public static final String PENDING_UPDATE_HASH_KEY = "hash"; 21 | public static final String PENDING_UPDATE_IS_LOADING_KEY = "isLoading"; 22 | public static final String PENDING_UPDATE_KEY = "CODE_PUSH_PENDING_UPDATE"; 23 | public static final String PREVIOUS_PACKAGE_KEY = "previousPackage"; 24 | public static final String REACT_NATIVE_LOG_TAG = "ReactNative"; 25 | public static final String RELATIVE_BUNDLE_PATH_KEY = "bundlePath"; 26 | public static final String STATUS_FILE = "codepush.json"; 27 | public static final String UNZIPPED_FOLDER_NAME = "unzipped"; 28 | public static final String CODE_PUSH_APK_BUILD_TIME_KEY = "CODE_PUSH_APK_BUILD_TIME"; 29 | public static final String BUNDLE_JWT_FILE = ".codepushrelease"; 30 | public static final String LATEST_ROLLBACK_INFO_KEY = "LATEST_ROLLBACK_INFO"; 31 | public static final String LATEST_ROLLBACK_PACKAGE_HASH_KEY = "packageHash"; 32 | public static final String LATEST_ROLLBACK_TIME_KEY = "time"; 33 | public static final String LATEST_ROLLBACK_COUNT_KEY = "count"; 34 | public static final String CLIENT_UNIQUE_ID_KEY = "clientUniqueId"; 35 | } 36 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushInstallMode.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | public enum CodePushInstallMode { 4 | IMMEDIATE(0), 5 | ON_NEXT_RESTART(1), 6 | ON_NEXT_RESUME(2), 7 | ON_NEXT_SUSPEND(3); 8 | 9 | private final int value; 10 | CodePushInstallMode(int value) { 11 | this.value = value; 12 | } 13 | public int getValue() { 14 | return this.value; 15 | } 16 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPublicKeyException.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | class CodePushInvalidPublicKeyException extends RuntimeException { 4 | 5 | public CodePushInvalidPublicKeyException(String message, Throwable cause) { 6 | super(message, cause); 7 | } 8 | 9 | public CodePushInvalidPublicKeyException(String message) { 10 | super(message); 11 | } 12 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidUpdateException.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | public class CodePushInvalidUpdateException extends RuntimeException { 4 | public CodePushInvalidUpdateException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushMalformedDataException.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | import java.net.MalformedURLException; 4 | 5 | public class CodePushMalformedDataException extends RuntimeException { 6 | public CodePushMalformedDataException(String path, Throwable cause) { 7 | super("Unable to parse contents of " + path + ", the file may be corrupted.", cause); 8 | } 9 | public CodePushMalformedDataException(String url, MalformedURLException cause) { 10 | super("The package has an invalid downloadUrl: " + url, cause); 11 | } 12 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushNotInitializedException.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | public final class CodePushNotInitializedException extends RuntimeException { 4 | 5 | public CodePushNotInitializedException(String message, Throwable cause) { 6 | super(message, cause); 7 | } 8 | 9 | public CodePushNotInitializedException(String message) { 10 | super(message); 11 | } 12 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushUnknownException.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | class CodePushUnknownException extends RuntimeException { 4 | 5 | public CodePushUnknownException(String message, Throwable cause) { 6 | super(message, cause); 7 | } 8 | 9 | public CodePushUnknownException(String message) { 10 | super(message); 11 | } 12 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateState.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | public enum CodePushUpdateState { 4 | RUNNING(0), 5 | PENDING(1), 6 | LATEST(2); 7 | 8 | private final int value; 9 | CodePushUpdateState(int value) { 10 | this.value = value; 11 | } 12 | public int getValue() { 13 | return this.value; 14 | } 15 | } -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/DownloadProgress.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | import com.facebook.react.bridge.WritableMap; 4 | import com.facebook.react.bridge.WritableNativeMap; 5 | 6 | class DownloadProgress { 7 | private long mTotalBytes; 8 | private long mReceivedBytes; 9 | 10 | public DownloadProgress (long totalBytes, long receivedBytes){ 11 | mTotalBytes = totalBytes; 12 | mReceivedBytes = receivedBytes; 13 | } 14 | 15 | public WritableMap createWritableMap() { 16 | WritableMap map = new WritableNativeMap(); 17 | if (mTotalBytes < Integer.MAX_VALUE) { 18 | map.putInt("totalBytes", (int) mTotalBytes); 19 | map.putInt("receivedBytes", (int) mReceivedBytes); 20 | } else { 21 | map.putDouble("totalBytes", mTotalBytes); 22 | map.putDouble("receivedBytes", mReceivedBytes); 23 | } 24 | return map; 25 | } 26 | 27 | public boolean isCompleted() { 28 | return mTotalBytes == mReceivedBytes; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressCallback.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | interface DownloadProgressCallback { 4 | void call(DownloadProgress downloadProgress); 5 | } 6 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/ReactInstanceHolder.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | import com.facebook.react.ReactInstanceManager; 4 | 5 | /** 6 | * Provides access to a {@link ReactInstanceManager}. 7 | * 8 | * ReactNativeHost already implements this interface, if you make use of that react-native 9 | * component (just add `implements ReactInstanceHolder`). 10 | */ 11 | public interface ReactInstanceHolder { 12 | 13 | /** 14 | * Get the current {@link ReactInstanceManager} instance. May return null. 15 | */ 16 | ReactInstanceManager getReactInstanceManager(); 17 | } 18 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/microsoft/codepush/react/TLSSocketFactory.java: -------------------------------------------------------------------------------- 1 | package com.microsoft.codepush.react; 2 | 3 | import java.io.IOException; 4 | import java.net.InetAddress; 5 | import java.net.Socket; 6 | import java.net.UnknownHostException; 7 | import java.security.KeyManagementException; 8 | import java.security.NoSuchAlgorithmException; 9 | 10 | import javax.net.ssl.SSLContext; 11 | import javax.net.ssl.SSLSocket; 12 | import javax.net.ssl.SSLSocketFactory; 13 | 14 | public class TLSSocketFactory extends SSLSocketFactory { 15 | 16 | private SSLSocketFactory delegate; 17 | 18 | public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { 19 | SSLContext context = SSLContext.getInstance("TLS"); 20 | context.init(null, null, null); 21 | delegate = context.getSocketFactory(); 22 | } 23 | 24 | @Override 25 | public String[] getDefaultCipherSuites() { 26 | return delegate.getDefaultCipherSuites(); 27 | } 28 | 29 | @Override 30 | public String[] getSupportedCipherSuites() { 31 | return delegate.getSupportedCipherSuites(); 32 | } 33 | 34 | @Override 35 | public Socket createSocket() throws IOException { 36 | return enableTLSOnSocket(delegate.createSocket()); 37 | } 38 | 39 | @Override 40 | public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { 41 | return enableTLSOnSocket(delegate.createSocket(s, host, port, autoClose)); 42 | } 43 | 44 | @Override 45 | public Socket createSocket(String host, int port) throws IOException, UnknownHostException { 46 | return enableTLSOnSocket(delegate.createSocket(host, port)); 47 | } 48 | 49 | @Override 50 | public Socket createSocket(String host, int port, InetAddress localHost, int localPort) 51 | throws IOException, UnknownHostException { 52 | return enableTLSOnSocket(delegate.createSocket(host, port, localHost, localPort)); 53 | } 54 | 55 | @Override 56 | public Socket createSocket(InetAddress host, int port) throws IOException { 57 | return enableTLSOnSocket(delegate.createSocket(host, port)); 58 | } 59 | 60 | @Override 61 | public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) 62 | throws IOException { 63 | return enableTLSOnSocket(delegate.createSocket(address, port, localAddress, localPort)); 64 | } 65 | 66 | private Socket enableTLSOnSocket(Socket socket) { 67 | if (socket != null && (socket instanceof SSLSocket)) { 68 | ((SSLSocket) socket).setEnabledProtocols(new String[] { "TLSv1.1", "TLSv1.2" }); 69 | } 70 | return socket; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | mavenCentral() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:1.3.0' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | mavenLocal() 19 | mavenCentral() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Soomgo-Mobile/react-native-code-push/932410be08904c0ec4315e1aa92d7508b0e9ed02/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip 6 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' -------------------------------------------------------------------------------- /babel-plugin-code-push/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bravemobile/babel-plugin-code-push", 3 | "version": "1.0.0", 4 | "description": "Babel plugin for @bravemobile/react-native-code-push", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "cd test && jest" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@bravemobile/react-native-code-push": "../" 14 | }, 15 | "devDependencies": { 16 | "@babel/cli": "^7.25.9", 17 | "@babel/core": "^7.26.0", 18 | "@babel/node": "^7.26.0", 19 | "@babel/preset-env": "^7.26.0", 20 | "@types/shelljs": "^0.8.15", 21 | "babel-cli": "^6.26.0", 22 | "babel-jest": "^29.7.0", 23 | "jest": "^29.7.0", 24 | "shelljs": "^0.8.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /babel-plugin-code-push/test/.babelrc.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | if (api.env("test")) { 3 | return { 4 | presets: [["@babel/preset-env", { targets: { node: "current" } }]], 5 | plugins: [["../index.js"]], 6 | }; 7 | } 8 | 9 | return { 10 | plugins: [["../index.js"]], 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /babel-plugin-code-push/test/cases/test1-config: -------------------------------------------------------------------------------- 1 | const { 2 | SemverVersioning, 3 | } = require("@bravemobile/react-native-code-push/versioning"); 4 | 5 | class CustomVersioning extends SemverVersioning { 6 | constructor() { 7 | super(); 8 | } 9 | } 10 | 11 | module.exports = { 12 | bundleHost: "bundleHost", 13 | runtimeVersion: "runtimeVersion", 14 | versioning: CustomVersioning, 15 | }; 16 | -------------------------------------------------------------------------------- /babel-plugin-code-push/test/cases/test1-input: -------------------------------------------------------------------------------- 1 | import codePush from "@bravemobile/react-native-code-push"; 2 | 3 | codePush(); 4 | -------------------------------------------------------------------------------- /babel-plugin-code-push/test/cases/test1-output: -------------------------------------------------------------------------------- 1 | import { SemverVersioning } from "@bravemobile/react-native-code-push/versioning"; 2 | import codePush from "@bravemobile/react-native-code-push"; 3 | codePush({ 4 | bundleHost: "bundleHost", 5 | runtimeVersion: "runtimeVersion", 6 | versioning: class CustomVersioning extends SemverVersioning { 7 | constructor() { 8 | super(); 9 | } 10 | } 11 | }); -------------------------------------------------------------------------------- /babel-plugin-code-push/test/cases/test2-config: -------------------------------------------------------------------------------- 1 | const { 2 | SemverVersioning, 3 | } = require("@bravemobile/react-native-code-push/versioning"); 4 | 5 | module.exports = { 6 | bundleHost: "bundleHost", 7 | runtimeVersion: "runtimeVersion", 8 | versioning: SemverVersioning, 9 | }; 10 | -------------------------------------------------------------------------------- /babel-plugin-code-push/test/cases/test2-input: -------------------------------------------------------------------------------- 1 | import codePush from "@bravemobile/react-native-code-push"; 2 | 3 | codePush(); 4 | -------------------------------------------------------------------------------- /babel-plugin-code-push/test/cases/test2-output: -------------------------------------------------------------------------------- 1 | import { SemverVersioning } from "@bravemobile/react-native-code-push/versioning"; 2 | import codePush from "@bravemobile/react-native-code-push"; 3 | codePush({ 4 | bundleHost: "bundleHost", 5 | runtimeVersion: "runtimeVersion", 6 | versioning: SemverVersioning 7 | }); -------------------------------------------------------------------------------- /babel-plugin-code-push/test/codepush.config.js: -------------------------------------------------------------------------------- 1 | const { 2 | SemverVersioning, 3 | } = require("@bravemobile/react-native-code-push/versioning"); 4 | 5 | class CustomVersioning extends SemverVersioning { 6 | constructor() { 7 | super(); 8 | } 9 | } 10 | 11 | module.exports = { 12 | bundleHost: "bundleHost", 13 | runtimeVersion: "runtimeVersion", 14 | versioning: CustomVersioning, 15 | }; 16 | -------------------------------------------------------------------------------- /babel-plugin-code-push/test/plugin.test.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import { transformSync } from "@babel/core"; 3 | 4 | describe("babel plugin", () => { 5 | test("case1: using custom Versioning class", () => { 6 | const input = fs.readFileSync("./cases/test1-input", { encoding: "utf-8" }); 7 | const expected = fs.readFileSync("./cases/test1-output", { 8 | encoding: "utf-8", 9 | }); 10 | 11 | const generated = transformSync(input, { 12 | plugins: [ 13 | [ 14 | "../index.js", 15 | { 16 | configPath: "./cases/test1-config", 17 | }, 18 | ], 19 | ], 20 | }); 21 | 22 | expect(generated.code).toBe(expected); 23 | }); 24 | 25 | test("case2: using provided Versioning class", () => { 26 | const input = fs.readFileSync("./cases/test2-input", { encoding: "utf-8" }); 27 | const expected = fs.readFileSync("./cases/test2-output", { 28 | encoding: "utf-8", 29 | }); 30 | 31 | const generated = transformSync(input, { 32 | plugins: [ 33 | [ 34 | "../index.js", 35 | { 36 | configPath: "./cases/test2-config", 37 | }, 38 | ], 39 | ], 40 | }); 41 | 42 | expect(generated.code).toBe(expected); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [['@babel/preset-env', {targets: {node: 'current'}}]], 3 | }; -------------------------------------------------------------------------------- /cli/commands/bundleCommand/bundleCodePush.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { prepareToBundleJS } = require('../../functions/prepareToBundleJS'); 3 | const { runReactNativeBundleCommand } = require('../../functions/runReactNativeBundleCommand'); 4 | const { getReactTempDir } = require('../../functions/getReactTempDir'); 5 | const { runHermesEmitBinaryCommand } = require('../../functions/runHermesEmitBinaryCommand'); 6 | const { makeCodePushBundle } = require('../../functions/makeCodePushBundle'); 7 | const { ROOT_OUTPUT_DIR, ENTRY_FILE } = require('../../constant'); 8 | 9 | /** 10 | * @param platform {string} 'ios' | 'android' 11 | * @param outputRootPath {string} 12 | * @param entryFile {string} 13 | * @param jsBundleName {string|undefined} 14 | * @param bundleDirectory {string} 15 | * @return {Promise} CodePush bundle file name (equals to packageHash) 16 | */ 17 | async function bundleCodePush( 18 | platform = 'ios', 19 | outputRootPath = ROOT_OUTPUT_DIR, 20 | entryFile = ENTRY_FILE, 21 | jsBundleName, // JS bundle file name (not CodePush bundle file) 22 | bundleDirectory, // CodePush bundle output directory 23 | ) { 24 | if (fs.existsSync(outputRootPath)) { 25 | fs.rmSync(outputRootPath, { recursive: true }); 26 | } 27 | 28 | const OUTPUT_CONTENT_PATH = `${outputRootPath}/CodePush`; 29 | const DEFAULT_JS_BUNDLE_NAME = platform === 'ios' ? 'main.jsbundle' : 'index.android.bundle'; 30 | const _jsBundleName = jsBundleName || DEFAULT_JS_BUNDLE_NAME; // react-native JS bundle output name 31 | const SOURCEMAP_OUTPUT = `${outputRootPath}/${_jsBundleName}.map`; 32 | 33 | prepareToBundleJS({ deleteDirs: [outputRootPath, getReactTempDir()], makeDir: OUTPUT_CONTENT_PATH }); 34 | 35 | runReactNativeBundleCommand( 36 | _jsBundleName, 37 | OUTPUT_CONTENT_PATH, 38 | platform, 39 | SOURCEMAP_OUTPUT, 40 | entryFile, 41 | ); 42 | console.log('log: JS bundling complete'); 43 | 44 | await runHermesEmitBinaryCommand( 45 | _jsBundleName, 46 | OUTPUT_CONTENT_PATH, 47 | SOURCEMAP_OUTPUT, 48 | ); 49 | console.log('log: Hermes compilation complete'); 50 | 51 | const { bundleFileName: codePushBundleFileName } = await makeCodePushBundle(OUTPUT_CONTENT_PATH, bundleDirectory); 52 | console.log(`log: CodePush bundle created (file path: ./${bundleDirectory}/${codePushBundleFileName})`); 53 | 54 | return codePushBundleFileName; 55 | } 56 | 57 | module.exports = { bundleCodePush: bundleCodePush }; 58 | -------------------------------------------------------------------------------- /cli/commands/bundleCommand/index.js: -------------------------------------------------------------------------------- 1 | const { program, Option } = require("commander"); 2 | const { bundleCodePush } = require("./bundleCodePush"); 3 | const { OUTPUT_BUNDLE_DIR, ROOT_OUTPUT_DIR, ENTRY_FILE } = require('../../constant'); 4 | 5 | program.command('bundle') 6 | .description('Creates a CodePush bundle file (assumes Hermes is enabled).') 7 | .addOption(new Option('-p, --platform ', 'platform').choices(['ios', 'android']).default('ios')) 8 | .option('-o, --output-path ', 'path to output root directory', ROOT_OUTPUT_DIR) 9 | .option('-e, --entry-file ', 'path to JS/TS entry file', ENTRY_FILE) 10 | .option('-b, --bundle-name ', 'bundle file name (default-ios: "main.jsbundle" / default-android: "index.android.bundle")') 11 | .option('--output-bundle-dir ', 'name of directory containing the bundle file created by the "bundle" command', OUTPUT_BUNDLE_DIR) 12 | /** 13 | * @param {Object} options 14 | * @param {string} options.platform 15 | * @param {string} options.outputPath 16 | * @param {string} options.entryFile 17 | * @param {string} options.bundleName 18 | * @param {string} options.outputBundleDir 19 | * @return {void} 20 | */ 21 | .action((options) => { 22 | bundleCodePush( 23 | options.platform, 24 | options.outputPath, 25 | options.entryFile, 26 | options.bundleName, 27 | `${options.outputPath}/${options.outputBundleDir}`, 28 | ) 29 | }); 30 | -------------------------------------------------------------------------------- /cli/commands/createHistoryCommand/createReleaseHistory.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | /** 5 | * 6 | * @param targetVersion {string} 7 | * @param setReleaseHistory { 8 | * function( 9 | * targetBinaryVersion: string, 10 | * jsonFilePath: string, 11 | * releaseInfo: ReleaseHistoryInterface, 12 | * platform: string, 13 | * identifier: string 14 | * ): Promise} 15 | * @param platform {"ios" | "android"} 16 | * @param identifier {string} 17 | * @returns {Promise} 18 | */ 19 | async function createReleaseHistory( 20 | targetVersion, 21 | setReleaseHistory, 22 | platform, 23 | identifier, 24 | ) { 25 | const BINARY_RELEASE = { 26 | enabled: true, 27 | mandatory: false, 28 | downloadUrl: "", 29 | packageHash: "", 30 | }; 31 | 32 | /** @type {ReleaseHistoryInterface} */ 33 | const INITIAL_HISTORY = { 34 | [targetVersion]: BINARY_RELEASE 35 | }; 36 | 37 | try { 38 | const JSON_FILE_NAME = `${targetVersion}.json`; 39 | const JSON_FILE_PATH = path.resolve(process.cwd(), JSON_FILE_NAME); 40 | 41 | console.log(`log: creating JSON file... ("${JSON_FILE_NAME}")\n`, JSON.stringify(INITIAL_HISTORY, null, 2)); 42 | fs.writeFileSync(JSON_FILE_PATH, JSON.stringify(INITIAL_HISTORY)); 43 | 44 | await setReleaseHistory(targetVersion, JSON_FILE_PATH, INITIAL_HISTORY, platform, identifier) 45 | 46 | fs.unlinkSync(JSON_FILE_PATH); 47 | } catch (error) { 48 | console.error('Error occurred while creating new history:', error); 49 | process.exit(1) 50 | } 51 | } 52 | 53 | module.exports = { createReleaseHistory: createReleaseHistory } 54 | -------------------------------------------------------------------------------- /cli/commands/createHistoryCommand/index.js: -------------------------------------------------------------------------------- 1 | const { program, Option } = require("commander"); 2 | const { findAndReadConfigFile } = require("../../utils/fsUtils"); 3 | const { createReleaseHistory } = require("./createReleaseHistory"); 4 | const { CONFIG_FILE_NAME } = require('../../constant'); 5 | 6 | program.command('create-history') 7 | .description('Creates a new release history for the binary app.') 8 | .requiredOption('-b, --binary-version ', '(Required) The target binary version') 9 | .addOption(new Option('-p, --platform ', 'platform').choices(['ios', 'android']).default('ios')) 10 | .option('-i, --identifier ', 'reserved characters to distinguish the release.') 11 | .option('-c, --config ', 'set config file name (JS/TS)', CONFIG_FILE_NAME) 12 | /** 13 | * @param {Object} options 14 | * @param {string} options.binaryVersion 15 | * @param {string} options.platform 16 | * @param {string} options.identifier 17 | * @param {string} options.config 18 | * @return {void} 19 | */ 20 | .action(async (options) => { 21 | const config = findAndReadConfigFile(process.cwd(), options.config); 22 | 23 | await createReleaseHistory( 24 | options.binaryVersion, 25 | config.setReleaseHistory, 26 | options.platform, 27 | options.identifier 28 | ); 29 | }); 30 | -------------------------------------------------------------------------------- /cli/commands/releaseCommand/addToReleaseHistory.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | 4 | /** 5 | * 6 | * @param appVersion {string} 7 | * @param binaryVersion {string} 8 | * @param bundleDownloadUrl {string} 9 | * @param packageHash {string} 10 | * @param getReleaseHistory { 11 | * function( 12 | * targetBinaryVersion: string, 13 | * platform: string, 14 | * identifier?: string 15 | * ): Promise} 16 | * @param setReleaseHistory { 17 | * function( 18 | * targetBinaryVersion: string, 19 | * jsonFilePath: string, 20 | * releaseInfo: ReleaseHistoryInterface, 21 | * platform: string, 22 | * identifier?: string 23 | * ): Promise} 24 | * @param platform {"ios" | "android"} 25 | * @param identifier {string?} 26 | * @param mandatory {boolean?} 27 | * @param enable {boolean?} 28 | * @returns {Promise} 29 | */ 30 | async function addToReleaseHistory( 31 | appVersion, 32 | binaryVersion, 33 | bundleDownloadUrl, 34 | packageHash, 35 | getReleaseHistory, 36 | setReleaseHistory, 37 | platform, 38 | identifier, 39 | mandatory, 40 | enable, 41 | ) { 42 | const releaseHistory = await getReleaseHistory(binaryVersion, platform, identifier); 43 | 44 | const updateInfo = releaseHistory[appVersion] 45 | if (updateInfo) { 46 | console.error(`v${appVersion} is already released`) 47 | process.exit(1) 48 | } 49 | 50 | const newReleaseHistory = structuredClone(releaseHistory); 51 | 52 | newReleaseHistory[appVersion] = { 53 | enabled: enable, 54 | mandatory: mandatory, 55 | downloadUrl: bundleDownloadUrl, 56 | packageHash: packageHash, 57 | } 58 | 59 | try { 60 | const JSON_FILE_NAME = `${binaryVersion}.json`; 61 | const JSON_FILE_PATH = path.resolve(process.cwd(), JSON_FILE_NAME); 62 | 63 | console.log(`log: creating JSON file... ("${JSON_FILE_NAME}")\n`, JSON.stringify(newReleaseHistory, null, 2)); 64 | fs.writeFileSync(JSON_FILE_PATH, JSON.stringify(newReleaseHistory)); 65 | 66 | await setReleaseHistory(binaryVersion, JSON_FILE_PATH, newReleaseHistory, platform, identifier) 67 | 68 | fs.unlinkSync(JSON_FILE_PATH); 69 | } catch (error) { 70 | console.error('Error occurred while updating history:', error); 71 | process.exit(1) 72 | } 73 | } 74 | 75 | module.exports = { addToReleaseHistory: addToReleaseHistory } 76 | -------------------------------------------------------------------------------- /cli/commands/showHistoryCommand/index.js: -------------------------------------------------------------------------------- 1 | const { program, Option } = require("commander"); 2 | const { findAndReadConfigFile } = require("../../utils/fsUtils"); 3 | const { CONFIG_FILE_NAME } = require('../../constant'); 4 | 5 | program.command('show-history') 6 | .description('Retrieves and prints the release history of a specific binary version.\n`getReleaseHistory` function should be implemented in the config file.') 7 | .requiredOption('-b, --binary-version ', '(Required) The target binary version for retrieving the release history.') 8 | .addOption(new Option('-p, --platform ', 'platform').choices(['ios', 'android']).default('ios')) 9 | .option('-i, --identifier ', 'reserved characters to distinguish the release.') 10 | .option('-c, --config ', 'configuration file name (JS/TS)', CONFIG_FILE_NAME) 11 | /** 12 | * @param {Object} options 13 | * @param {string} options.binaryVersion 14 | * @param {string} options.platform 15 | * @param {string} options.identifier 16 | * @param {string} options.config 17 | * @return {void} 18 | */ 19 | .action(async (options) => { 20 | const config = findAndReadConfigFile(process.cwd(), options.config); 21 | 22 | const releaseHistory = await config.getReleaseHistory( 23 | options.binaryVersion, 24 | options.platform, 25 | options.identifier 26 | ); 27 | 28 | console.log(JSON.stringify(releaseHistory, null, 2)); 29 | }); 30 | -------------------------------------------------------------------------------- /cli/commands/updateHistoryCommand/index.js: -------------------------------------------------------------------------------- 1 | const { program, Option } = require("commander"); 2 | const { findAndReadConfigFile } = require("../../utils/fsUtils"); 3 | const { updateReleaseHistory } = require("./updateReleaseHistory"); 4 | const { CONFIG_FILE_NAME } = require('../../constant'); 5 | 6 | program.command('update-history') 7 | .description('Updates the release history for a specific binary version.\n`getReleaseHistory`, `setReleaseHistory` functions should be implemented in the config file.') 8 | .requiredOption('-v, --app-version ', '(Required) The app version for which update information is to be modified.') 9 | .requiredOption('-b, --binary-version ', '(Required) The target binary version of the app for which update information is to be modified.') 10 | .addOption(new Option('-p, --platform ', 'platform').choices(['ios', 'android']).default('ios')) 11 | .option('-i, --identifier ', 'reserved characters to distinguish the release.') 12 | .option('-c, --config ', 'set config file name (JS/TS)', CONFIG_FILE_NAME) 13 | .option('-m, --mandatory ', 'make the release to be mandatory', parseBoolean, undefined) 14 | .option('-e, --enable ', 'make the release to be enabled', parseBoolean, undefined) 15 | /** 16 | * @param {Object} options 17 | * @param {string} options.appVersion 18 | * @param {string} options.binaryVersion 19 | * @param {string} options.platform 20 | * @param {string} options.identifier 21 | * @param {string} options.config 22 | * @param {string} options.mandatory 23 | * @param {string} options.enable 24 | * @return {void} 25 | */ 26 | .action(async (options) => { 27 | const config = findAndReadConfigFile(process.cwd(), options.config); 28 | 29 | if (typeof options.mandatory !== "boolean" && typeof options.enable !== "boolean") { 30 | console.error('No options specified. Exiting the program.') 31 | process.exit(1) 32 | } 33 | 34 | await updateReleaseHistory( 35 | options.appVersion, 36 | options.binaryVersion, 37 | config.getReleaseHistory, 38 | config.setReleaseHistory, 39 | options.platform, 40 | options.identifier, 41 | options.mandatory, 42 | options.enable, 43 | ) 44 | }); 45 | 46 | function parseBoolean(value) { 47 | if (value === 'true') return true; 48 | if (value === 'false') return false; 49 | else return undefined; 50 | } 51 | -------------------------------------------------------------------------------- /cli/commands/updateHistoryCommand/updateReleaseHistory.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | /** 5 | * 6 | * @param appVersion {string} 7 | * @param binaryVersion {string} 8 | * @param getReleaseHistory { 9 | * function( 10 | * targetBinaryVersion: string, 11 | * platform: string, 12 | * identifier?: string 13 | * ): Promise} 14 | * @param setReleaseHistory { 15 | * function( 16 | * targetBinaryVersion: string, 17 | * jsonFilePath: string, 18 | * releaseInfo: ReleaseHistoryInterface, 19 | * platform: string, 20 | * identifier?: string 21 | * ): Promise} 22 | * @param platform {"ios" | "android"} 23 | * @param identifier {string?} 24 | * @param mandatory {boolean?} 25 | * @param enable {boolean?} 26 | * @returns {Promise} 27 | */ 28 | async function updateReleaseHistory( 29 | appVersion, 30 | binaryVersion, 31 | getReleaseHistory, 32 | setReleaseHistory, 33 | platform, 34 | identifier, 35 | mandatory, 36 | enable, 37 | ) { 38 | const releaseHistory = await getReleaseHistory(binaryVersion, platform, identifier); 39 | 40 | const updateInfo = releaseHistory[appVersion] 41 | if (!updateInfo) throw new Error(`v${appVersion} is not released`) 42 | 43 | if (typeof mandatory === "boolean") updateInfo.mandatory = mandatory; 44 | if (typeof enable === "boolean") updateInfo.enabled = enable; 45 | 46 | try { 47 | const JSON_FILE_NAME = `${binaryVersion}.json`; 48 | const JSON_FILE_PATH = path.resolve(process.cwd(), JSON_FILE_NAME); 49 | 50 | console.log(`log: creating JSON file... ("${JSON_FILE_NAME}")\n`, JSON.stringify(releaseHistory, null, 2)); 51 | fs.writeFileSync(JSON_FILE_PATH, JSON.stringify(releaseHistory)); 52 | 53 | await setReleaseHistory(binaryVersion, JSON_FILE_PATH, releaseHistory, platform, identifier) 54 | 55 | fs.unlinkSync(JSON_FILE_PATH); 56 | } catch (error) { 57 | console.error('Error occurred while updating history:', error); 58 | process.exit(1) 59 | } 60 | } 61 | 62 | module.exports = { updateReleaseHistory: updateReleaseHistory } 63 | -------------------------------------------------------------------------------- /cli/constant.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | CONFIG_FILE_NAME: "code-push.config.ts", 3 | OUTPUT_BUNDLE_DIR: "bundleOutput", 4 | ROOT_OUTPUT_DIR: "build", 5 | ENTRY_FILE: "index.ts", 6 | }; 7 | -------------------------------------------------------------------------------- /cli/functions/getReactTempDir.js: -------------------------------------------------------------------------------- 1 | /** 2 | * code based on appcenter-cli 3 | */ 4 | 5 | const os = require('os'); 6 | 7 | /** 8 | * Return the path of the temporary directory for react-native bundling 9 | * 10 | * @return {string} 11 | */ 12 | function getReactTempDir() { 13 | return `${os.tmpdir()}/react-*`; 14 | } 15 | 16 | module.exports = { getReactTempDir }; 17 | -------------------------------------------------------------------------------- /cli/functions/makeCodePushBundle.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const shell = require('shelljs'); 3 | const zip = require('../utils/zip'); 4 | const {generatePackageHashFromDirectory} = require('../utils/hash-utils'); 5 | 6 | /** 7 | * Create a CodePush bundle file and return the information. 8 | * 9 | * @param contentsPath {string} The directory path containing the contents to be made into a CodePush bundle (usually the 'build/CodePush' directory)) 10 | * @param bundleDirectory {string} The directory path to save the CodePush bundle file 11 | * @return {Promise<{ bundleFileName: string }>} 12 | */ 13 | async function makeCodePushBundle(contentsPath, bundleDirectory) { 14 | const updateContentsZipPath = await zip(contentsPath); 15 | 16 | const packageHash = await generatePackageHashFromDirectory(contentsPath, path.join(contentsPath, '..')); 17 | 18 | shell.mkdir('-p', `./${bundleDirectory}`); 19 | shell.mv(updateContentsZipPath, `./${bundleDirectory}/${packageHash}`); 20 | 21 | return { 22 | // To allow the "release" command to get the file and hash value from the result of the "bundle" command, 23 | // use the hash value as the name of the file. 24 | bundleFileName: packageHash, 25 | }; 26 | } 27 | 28 | module.exports = { makeCodePushBundle }; 29 | -------------------------------------------------------------------------------- /cli/functions/prepareToBundleJS.js: -------------------------------------------------------------------------------- 1 | const shell = require('shelljs'); 2 | 3 | /** 4 | * @param deleteDirs {string[]} Directories to delete 5 | * @param makeDir {string} Directory path to create 6 | */ 7 | function prepareToBundleJS({ deleteDirs, makeDir }) { 8 | shell.rm('-rf', deleteDirs); 9 | shell.mkdir('-p', makeDir); 10 | } 11 | 12 | module.exports = { prepareToBundleJS }; 13 | -------------------------------------------------------------------------------- /cli/functions/runReactNativeBundleCommand.js: -------------------------------------------------------------------------------- 1 | /** 2 | * code based on appcenter-cli 3 | */ 4 | 5 | const path = require('path'); 6 | const shell = require('shelljs'); 7 | 8 | /** 9 | * Run `react-native bundle` CLI command 10 | * 11 | * @param bundleName {string} JS bundle file name 12 | * @param entryFile {string} App code entry file name (default: index.ts) 13 | * @param outputPath {string} Path to output JS bundle file and assets 14 | * @param platform {string} Platform (ios | android) 15 | * @param sourcemapOutput {string} Path to output sourcemap file (Warning: if sourcemapOutput points to the outputPath, the sourcemap will be included in the CodePush bundle and increase the deployment size) 16 | * @param extraBundlerOptions {string[]} Additional options to pass to `react-native bundle` command 17 | * @return {void} 18 | */ 19 | function runReactNativeBundleCommand( 20 | bundleName, 21 | outputPath, 22 | platform, 23 | sourcemapOutput, 24 | entryFile, 25 | extraBundlerOptions = [], 26 | ) { 27 | /** 28 | * @return {string} 29 | */ 30 | function getCliPath() { 31 | return path.join('node_modules', '.bin', 'react-native'); 32 | } 33 | 34 | /** 35 | * @type {string[]} 36 | */ 37 | const reactNativeBundleArgs = [ 38 | 'bundle', 39 | '--assets-dest', 40 | outputPath, 41 | '--bundle-output', 42 | path.join(outputPath, bundleName), 43 | '--dev', 44 | 'false', 45 | '--entry-file', 46 | entryFile, 47 | '--platform', 48 | platform, 49 | '--sourcemap-output', 50 | sourcemapOutput, 51 | ...extraBundlerOptions, 52 | ]; 53 | 54 | console.log('Running "react-native bundle" command:\n'); 55 | 56 | shell.exec(`${getCliPath()} ${reactNativeBundleArgs.join(' ')}`); 57 | } 58 | 59 | module.exports = { runReactNativeBundleCommand }; 60 | -------------------------------------------------------------------------------- /cli/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const { program } = require("commander"); 4 | const shell = require("shelljs"); 5 | const { showLogo } = require("./utils/showLogo"); 6 | 7 | shell.set("-e"); 8 | shell.set("+v"); 9 | 10 | program 11 | .name("npx code-push") 12 | .description("Command line interface for @bravemobile/react-native-code-push") 13 | .version("1.0.0") 14 | .action(() => { 15 | showLogo(); 16 | }); 17 | 18 | /** 19 | * npx code-push bundle 20 | */ 21 | require("./commands/bundleCommand"); 22 | 23 | /** 24 | * npx code-push create-history 25 | */ 26 | require('./commands/createHistoryCommand'); 27 | 28 | /** 29 | * npx code-push update-history 30 | */ 31 | require('./commands/updateHistoryCommand'); 32 | 33 | /** 34 | * npx code-push release 35 | */ 36 | require('./commands/releaseCommand'); 37 | 38 | /** 39 | * npx code-push show-history 40 | */ 41 | require('./commands/showHistoryCommand') 42 | 43 | program.parse(); 44 | -------------------------------------------------------------------------------- /cli/utils/file-utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * code based on appcenter-cli 3 | */ 4 | 5 | const fs = require('fs'); 6 | 7 | /** 8 | * 9 | * @param path {string} 10 | * @return {boolean} 11 | */ 12 | function isDirectory(path) { 13 | return fs.statSync(path).isDirectory(); 14 | } 15 | 16 | /** 17 | * 18 | * @param length {number} 19 | * @return {string} 20 | */ 21 | function generateRandomFilename(length) { 22 | let filename = ''; 23 | const validChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 24 | 25 | for (let i = 0; i < length; i++) { 26 | filename += validChar.charAt(Math.floor(Math.random() * validChar.length)); 27 | } 28 | 29 | return filename; 30 | } 31 | 32 | /** 33 | * 34 | * @param filePath {string} 35 | * @return {string} 36 | */ 37 | function normalizePath(filePath) { 38 | //replace all backslashes coming from cli running on windows machines by slashes 39 | return filePath.replace(/\\/g, '/'); 40 | } 41 | 42 | module.exports = { isDirectory, generateRandomFilename, normalizePath }; 43 | -------------------------------------------------------------------------------- /cli/utils/fsUtils.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | /** 5 | * allows to require a config file with .ts extension 6 | * @param filePath {string} 7 | * @returns {*} FIXME type 8 | */ 9 | function requireConfig(filePath) { 10 | const ext = path.extname(filePath); 11 | 12 | if (ext === '.ts') { 13 | try { 14 | require('ts-node/register'); 15 | } catch { 16 | console.error('ts-node not found. Please install ts-node as a devDependency.'); 17 | process.exit(1); 18 | } 19 | } else if (ext === '.js') { 20 | // do nothing 21 | } else { 22 | throw new Error(`Unsupported file extension: ${ext}`); 23 | } 24 | 25 | return require(filePath); 26 | } 27 | 28 | /** 29 | * @param startDir {string} 30 | * @param configFileName {string} 31 | * @returns {*|null} FIXME type 32 | */ 33 | function findAndReadConfigFile(startDir, configFileName) { 34 | let dir = startDir; 35 | 36 | while (dir !== path.parse(dir).root) { 37 | const configPath = path.join(dir, configFileName); 38 | if (fs.existsSync(configPath)) { 39 | const config = requireConfig(configPath); 40 | return config; 41 | } 42 | dir = path.dirname(dir); 43 | } 44 | 45 | console.error(`${configFileName} not found.`); 46 | return null; 47 | } 48 | 49 | module.exports = { findAndReadConfigFile }; 50 | -------------------------------------------------------------------------------- /cli/utils/promisfied-fs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * code based on appcenter-cli 3 | */ 4 | 5 | const { promises: fs } = require('fs'); 6 | const path = require('path'); 7 | 8 | /** 9 | * 10 | * @param dir {string} 11 | * @return {Promise} 12 | */ 13 | async function walk(dir) { 14 | const stats = await fs.stat(dir); 15 | if (stats.isDirectory()) { 16 | /** 17 | * @type {string[]} 18 | */ 19 | let files = []; 20 | for (const file of await fs.readdir(dir)) { 21 | files = files.concat(await walk(path.join(dir, file))); 22 | } 23 | return files; 24 | } else { 25 | return [dir]; 26 | } 27 | } 28 | 29 | module.exports = { walk }; 30 | -------------------------------------------------------------------------------- /cli/utils/showLogo.js: -------------------------------------------------------------------------------- 1 | function showLogo() { 2 | const logo = ` 3 | 4 | +------------------------------------------------------------------+ 5 | | | 6 | | ______ __ ____ __ | 7 | | / ____/___ ____/ /__ / __ \\__ _______/ /_ | 8 | | / / / __ \\/ __ / _ \\/ /_/ / / / / ___/ __ \\ | 9 | | / /___/ /_/ / /_/ / __/ ____/ /_/ (__ ) / / / | 10 | | \\____/\\____/\\__,_/\\___/_/ \\__,_/____/_/ /_/ | 11 | | | 12 | | | 13 | | 🚀 @bravemobile/react-native-code-push | 14 | +------------------------------------------------------------------+ 15 | 16 | Please refer to help command for more information. 17 | 18 | (interactive mode is not supported yet.) 19 | `; 20 | console.log(logo); 21 | } 22 | 23 | module.exports = { showLogo }; 24 | -------------------------------------------------------------------------------- /cli/utils/zip.js: -------------------------------------------------------------------------------- 1 | /** 2 | * code based on appcenter-cli 3 | */ 4 | 5 | const fs = require('fs'); 6 | const path = require('path'); 7 | const yazl = require('yazl'); 8 | const { generateRandomFilename, normalizePath, isDirectory } = require('./file-utils'); 9 | const { walk } = require('./promisfied-fs'); 10 | 11 | /** 12 | * @typedef {{ sourceLocation: string, targetLocation: string }} ReleaseFile 13 | */ 14 | 15 | /** 16 | * @param updateContentsPath {string} 17 | * @return {Promise} 18 | */ 19 | function zip(updateContentsPath) { 20 | 21 | // eslint-disable-next-line no-async-promise-executor 22 | return new Promise(async (resolve, reject) => { 23 | /** 24 | * @type {ReleaseFile[]} 25 | */ 26 | const releaseFiles = []; 27 | 28 | try { 29 | if (!isDirectory(updateContentsPath)) { 30 | releaseFiles.push({ 31 | sourceLocation: updateContentsPath, 32 | targetLocation: normalizePath(path.basename(updateContentsPath)), // Put the file in the root 33 | }); 34 | } 35 | } catch (error) { 36 | error.message = error.message + ' Make sure you have added the platform you are making a release to.`.'; 37 | reject(error); 38 | } 39 | 40 | /** 41 | * @type {string} 42 | */ 43 | const directoryPath = updateContentsPath; 44 | const baseDirectoryPath = path.join(directoryPath, '..'); // For legacy reasons, put the root directory in the zip 45 | 46 | /** 47 | * @type {string[]} 48 | */ 49 | const files = await walk(updateContentsPath); 50 | 51 | files.forEach((filePath) => { 52 | /** 53 | * @type {string} 54 | */ 55 | const relativePath = path.relative(baseDirectoryPath, filePath); 56 | releaseFiles.push({ 57 | sourceLocation: filePath, 58 | targetLocation: normalizePath(relativePath), 59 | }); 60 | }); 61 | 62 | /** 63 | * @type {string} 64 | */ 65 | const packagePath = path.join(process.cwd(), generateRandomFilename(15) + '.zip'); 66 | const zipFile = new yazl.ZipFile(); 67 | /** 68 | * @type {fs.WriteStream} 69 | */ 70 | const writeStream = fs.createWriteStream(packagePath); 71 | 72 | zipFile.outputStream 73 | .pipe(writeStream) 74 | .on('error', (error) => { 75 | reject(error); 76 | }) 77 | .on('close', () => { 78 | resolve(packagePath); 79 | }); 80 | 81 | releaseFiles.forEach((releaseFile) => { 82 | zipFile.addFile(releaseFile.sourceLocation, releaseFile.targetLocation); 83 | }); 84 | 85 | zipFile.end(); 86 | }); 87 | } 88 | 89 | module.exports = zip; 90 | -------------------------------------------------------------------------------- /code-push-plugin-testing-framework/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code-push-plugin-testing-framework", 3 | "version": "0.0.1", 4 | "description": "Plugin Testing Framework for CodePush Plugins", 5 | "main": "script/index.js", 6 | "scripts": { 7 | "test": "gulp" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/microsoft/code-push.git" 12 | }, 13 | "author": { 14 | "name": "Microsoft Corporation" 15 | }, 16 | "license": "MIT", 17 | "homepage": "https://microsoft.github.io/code-push", 18 | "dependencies": { 19 | "@types/uuid": "^8.3.1", 20 | "base-64": "^1.0.0", 21 | "mocha": "latest", 22 | "mocha-junit-reporter": "latest", 23 | "q": "^1.5.1", 24 | "replace": "latest", 25 | "superagent": "^6.1.0", 26 | "superagent-proxy": "^3.0.0", 27 | "uuid": "^8.3.2" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/microsoft/code-push/issues" 31 | }, 32 | "readme": "ERROR: No README data found!", 33 | "_id": "code-push-plugin-testing-framework@0.0.1", 34 | "_shasum": "6ea33a661710628af266d714949fe95f88d71f0d", 35 | "_from": "../code-push/plugin-testing-framework/bin", 36 | "_resolved": "file:../code-push/plugin-testing-framework/bin" 37 | } 38 | -------------------------------------------------------------------------------- /code-push-plugin-testing-framework/script/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var Platform = require("./platform"); 3 | exports.Platform = Platform; 4 | var PluginTestingFramework = require("./test"); 5 | exports.PluginTestingFramework = PluginTestingFramework; 6 | var projectManager_1 = require("./projectManager"); 7 | exports.ProjectManager = projectManager_1.ProjectManager; 8 | exports.setupTestRunScenario = projectManager_1.setupTestRunScenario; 9 | exports.setupUpdateScenario = projectManager_1.setupUpdateScenario; 10 | var ServerUtil = require("./serverUtil"); 11 | exports.ServerUtil = ServerUtil; 12 | var testBuilder_1 = require("./testBuilder"); 13 | exports.TestBuilder = testBuilder_1.TestBuilder; 14 | var TestConfig = require("./testConfig"); 15 | exports.TestConfig = TestConfig; 16 | var testUtil_1 = require("./testUtil"); 17 | exports.TestUtil = testUtil_1.TestUtil; 18 | -------------------------------------------------------------------------------- /code-push-plugin-testing-framework/script/testConfig.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // IMPORTS // 3 | var os = require("os"); 4 | var path = require("path"); 5 | var TestUtil_1 = require("./testUtil"); 6 | ////////////////////////////////////////////////////////////////////////////////////////// 7 | // Configuration variables. 8 | // What plugin to use, what project directories to use, etc. 9 | // COMMAND LINE OPTION NAMES, FLAGS, AND DEFAULTS 10 | var DEFAULT_TEST_RUN_DIRECTORY = path.join(os.tmpdir(), TestUtil_1.TestUtil.getPluginName(), "test-run"); 11 | var DEFAULT_UPDATES_DIRECTORY = path.join(os.tmpdir(), TestUtil_1.TestUtil.getPluginName(), "updates"); 12 | var DEFAULT_PLUGIN_PATH = path.join(__dirname, "../.."); 13 | var NPM_PLUGIN_PATH = TestUtil_1.TestUtil.getPluginName(); 14 | var SETUP_FLAG_NAME = "--setup"; 15 | var DEFAULT_PLUGIN_TGZ_NAME = TestUtil_1.TestUtil.getPluginName() + "-" + TestUtil_1.TestUtil.getPluginVersion() + ".tgz"; 16 | // CONST VARIABLES 17 | exports.TestAppName = "TestCodePush"; 18 | exports.TestNamespace = "com.testcodepush"; 19 | exports.AcquisitionSDKPluginName = "code-push"; 20 | exports.templatePath = path.join(__dirname, "../../test/template"); 21 | exports.thisPluginInstallString = TestUtil_1.TestUtil.resolveBooleanVariables(process.env.NPM) ? `npm install ${NPM_PLUGIN_PATH}` : `npm pack ${DEFAULT_PLUGIN_PATH} && npm install ${DEFAULT_PLUGIN_TGZ_NAME} && npm link`; 22 | exports.testRunDirectory = process.env.RUN_DIR ? process.env.RUN_DIR: DEFAULT_TEST_RUN_DIRECTORY; 23 | exports.updatesDirectory = process.env.UPDATE_DIR ? process.env.UPDATE_DIR : DEFAULT_UPDATES_DIRECTORY; 24 | exports.onlyRunCoreTests = TestUtil_1.TestUtil.resolveBooleanVariables(process.env.CORE); 25 | exports.shouldSetup = TestUtil_1.TestUtil.readMochaCommandLineFlag(SETUP_FLAG_NAME); 26 | exports.restartEmulators = TestUtil_1.TestUtil.resolveBooleanVariables(process.env.CLEAN); 27 | -------------------------------------------------------------------------------- /docs/api-ios.md: -------------------------------------------------------------------------------- 1 | ### Objective-C API Reference (iOS) 2 | 3 | The Objective-C API is made available by importing the `CodePush.h` header into your `AppDelegate.m` file, and consists of a single public class named `CodePush`. 4 | 5 | #### CodePush 6 | 7 | Contains static methods for retreiving the `NSURL` that represents the most recent JavaScript bundle file, and can be passed to the `RCTRootView`'s `initWithBundleURL` method when bootstrapping your app in the `AppDelegate.m` file. 8 | 9 | The `CodePush` class' methods can be thought of as composite resolvers which always load the appropriate bundle, in order to accommodate the following scenarios: 10 | 11 | 1. When an end-user installs your app from the store (like `1.0.0`), they will get the JS bundle that is contained within the binary. This is the behavior you would get without using CodePush, but we make sure it doesn't break :) 12 | 13 | 2. As soon as you begin releasing CodePush updates, your end-users will get the JS bundle that represents the latest release for the configured deployment. This is the behavior that allows you to iterate beyond what you shipped to the store. 14 | 15 | 3. As soon as you release an update to the app store (like `1.1.0`), and your end-users update it, they will once again get the JS bundle that is contained within the binary. This behavior ensures that CodePush updates that targetted a previous binary version aren't used (since we don't know if they would work), and your end-users always have a working version of your app. 16 | 17 | 4. Repeat #2 and #3 as the CodePush releases and app store releases continue on into infinity (and beyond?) 18 | 19 | Because of this behavior, you can safely deploy updates to both the app store(s) and CodePush as necesary, and rest assured that your end-users will always get the most recent version. 20 | 21 | ##### Methods 22 | 23 | - __(NSURL \*)bundleURL__ - Returns the most recent JS bundle `NSURL` as described above. This method assumes that the name of the JS bundle contained within your app binary is `main.jsbundle`. 24 | 25 | - __(NSURL \*)bundleURLForResource:(NSString \*)resourceName__ - Equivalent to the `bundleURL` method, but also allows customizing the name of the JS bundle that is looked for within the app binary. This is useful if you aren't naming this file `main` (which is the default convention). This method assumes that the JS bundle's extension is `*.jsbundle`. 26 | 27 | - __(NSURL \*)bundleURLForResource:(NSString \*)resourceName withExtension:(NSString \*)resourceExtension__: Equivalent to the `bundleURLForResource:` method, but also allows customizing the extension used by the JS bundle that is looked for within the app binary. This is useful if you aren't naming this file `*.jsbundle` (which is the default convention). 28 | 29 | - __(void)overrideAppVersion:(NSString \*)appVersionOverride__ - Sets the version of the application's binary interface, which would otherwise default to the App Store version specified as the `CFBundleShortVersionString` in the `Info.plist`. This should be called a single time, before the bundle URL is loaded. 30 | 31 | - __(void)setDeploymentKey:(NSString \*)deploymentKey__ - Sets the deployment key that the app should use when querying for updates. This is a dynamic alternative to setting the deployment key in your `Info.plist` and/or specifying a deployment key in JS when calling `checkForUpdate` or `sync`. 32 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | import pluginReact from "eslint-plugin-react"; 5 | 6 | 7 | export default [ 8 | {files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]}, 9 | {languageOptions: { globals: { 10 | ...globals.node, 11 | ...globals.mocha, 12 | ...globals.browser, 13 | ...globals.jest 14 | } }}, 15 | pluginJs.configs.recommended, 16 | ...tseslint.configs.recommended, 17 | pluginReact.configs.flat.recommended, 18 | {"rules": { 19 | "@typescript-eslint/no-require-imports": "warn", 20 | "@typescript-eslint/no-unused-vars": "warn", 21 | "@typescript-eslint/no-unused-expressions": "warn", 22 | "no-empty": "warn", 23 | "@typescript-eslint/no-explicit-any": "warn", 24 | "no-var": "warn", 25 | "@typescript-eslint/no-this-alias": "warn", 26 | "@typescript-eslint/no-empty-object-type": "warn", 27 | "react/no-deprecated": "warn", 28 | "no-extra-boolean-cast": "warn", 29 | "no-useless-escape": "warn", 30 | "no-control-regex": "warn", 31 | }} 32 | ]; 33 | -------------------------------------------------------------------------------- /ios/CodePush.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/CodePush.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/CodePush/Base64/Base64/MF_Base64Additions.h: -------------------------------------------------------------------------------- 1 | // 2 | // MF_Base64Additions.h 3 | // Base64 -- RFC 4648 compatible implementation 4 | // see http://www.ietf.org/rfc/rfc4648.txt for more details 5 | // 6 | // Designed to be compiled with Automatic Reference Counting 7 | // 8 | // Created by Dave Poirier on 2012-06-14. 9 | // Public Domain 10 | // Hosted at https://github.com/ekscrypto/Base64 11 | // 12 | 13 | #import 14 | 15 | @interface NSString (Base64Addition) 16 | +(NSString *)stringFromBase64String:(NSString *)base64String; 17 | +(NSString *)stringFromBase64UrlEncodedString:(NSString *)base64UrlEncodedString; 18 | -(NSString *)base64String; 19 | -(NSString *)base64UrlEncodedString; 20 | @end 21 | 22 | @interface NSData (Base64Addition) 23 | +(NSData *)dataWithBase64String:(NSString *)base64String; 24 | +(NSData *)dataWithBase64UrlEncodedString:(NSString *)base64UrlEncodedString; 25 | -(NSString *)base64String; 26 | -(NSString *)base64UrlEncodedString; 27 | @end 28 | 29 | @interface MF_Base64Codec : NSObject 30 | +(NSData *)dataFromBase64String:(NSString *)base64String; 31 | +(NSString *)base64StringFromData:(NSData *)data; 32 | +(NSString *)base64UrlEncodedStringFromBase64String:(NSString *)base64String; 33 | +(NSString *)base64StringFromBase64UrlEncodedString:(NSString *)base64UrlEncodedString; 34 | @end 35 | -------------------------------------------------------------------------------- /ios/CodePush/Base64/README.md: -------------------------------------------------------------------------------- 1 | [![CI Status](https://travis-ci.org/ekscrypto/Base64.svg?branch=master)](https://github.com/ekscrypto/Base64) 2 | 3 | Base64 Additions for Objective-C on Mac OS X and iOS 4 | ======= 5 | 6 | 7 | Usage 8 | ---- 9 | Open the Xcode project file, and drag MF_Base64Additions.m/.h into your project. 10 | 11 | In files where you want to use Base64 encoding/decoding, simply include the header file and use one of the provided NSData or NSString additions. 12 | 13 | Example use: 14 | #import "MF_Base64Additions.h" 15 | 16 | NSString *helloWorld = @"Hello World"; 17 | NSString *helloInBase64 = [helloWorld base64String]; 18 | NSString *helloDecoded = [NSString stringFromBase64String:helloInBase64]; 19 | 20 | 21 | 22 | 23 | Performance 24 | ---- 25 | * Encoding: Approximately 4 to 5 times faster than using the equivalent SecTransform. 26 | * Encoding: 30% faster than https://github.com/l4u/NSData-Base64 27 | * Decoding: 5% faster than using the equivalent SecTransform. 28 | * Decoding: 5% faster than https://github.com/l4u/NSData-Base64 29 | 30 | 31 | 32 | Requirements 33 | ----- 34 | * Compile with Automatic Reference Counting 35 | * Compatible with Mac OSX 10.6+ and iOS 4.0+ 36 | 37 | 38 | 39 | Implementation 40 | ---- 41 | * Implemented as per RFC 4648, see http://www.ietf.org/rfc/rfc4648.txt for more details. 42 | 43 | 44 | 45 | Licensing 46 | ---- 47 | * Public Domain 48 | -------------------------------------------------------------------------------- /ios/CodePush/CodePushErrorUtils.m: -------------------------------------------------------------------------------- 1 | #import "CodePush.h" 2 | 3 | @implementation CodePushErrorUtils 4 | 5 | static NSString *const CodePushErrorDomain = @"CodePushError"; 6 | static const int CodePushErrorCode = -1; 7 | 8 | + (NSError *)errorWithMessage:(NSString *)errorMessage 9 | { 10 | return [NSError errorWithDomain:CodePushErrorDomain 11 | code:CodePushErrorCode 12 | userInfo:@{ NSLocalizedDescriptionKey: NSLocalizedString(errorMessage, nil) }]; 13 | } 14 | 15 | + (BOOL)isCodePushError:(NSError *)err 16 | { 17 | return err != nil && [CodePushErrorDomain isEqualToString:err.domain]; 18 | } 19 | 20 | @end -------------------------------------------------------------------------------- /ios/CodePush/CodePushUtils.m: -------------------------------------------------------------------------------- 1 | #import "CodePush.h" 2 | 3 | void CPLog(NSString *formatString, ...) { 4 | va_list args; 5 | va_start(args, formatString); 6 | NSString *prependedFormatString = [NSString stringWithFormat:@"\n[CodePush] %@", formatString]; 7 | NSLogv(prependedFormatString, args); 8 | va_end(args); 9 | } -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithm.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithm.h 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JWTDeprecations.h" 11 | 12 | @protocol JWTAlgorithm 13 | 14 | @required 15 | /** 16 | Signs data using provided secret data. 17 | @param hash The data to sign. 18 | @param key The secret to use for signing. 19 | @param error The inout error. 20 | */ 21 | - (NSData *)signHash:(NSData *)hash key:(NSData *)key error:(NSError *__autoreleasing*)error; 22 | /** 23 | Verifies data using. 24 | @param hash The data to sign. 25 | @param signature The secret to use for signing. 26 | @param error The inout error. 27 | */ 28 | - (BOOL)verifyHash:(NSData *)hash signature:(NSData *)signature key:(NSData *)key error:(NSError *__autoreleasing*)error; 29 | 30 | //@required 31 | 32 | @property (nonatomic, readonly, copy) NSString *name; 33 | 34 | /** 35 | Encodes and encrypts the provided payload using the provided secret key 36 | @param theString The string to encode 37 | @param theSecret The secret to use for encryption 38 | @return An NSData object containing the encrypted payload, or nil if something went wrong. 39 | */ 40 | - (NSData *)encodePayload:(NSString *)theString withSecret:(NSString *)theSecret __deprecated_and_will_be_removed_in_release_version(JWTVersion_3_0_0); 41 | 42 | /** 43 | Verifies the provided signature using the signed input and verification key 44 | @param input The header and payload encoded string 45 | @param signature The JWT provided signature 46 | @param verificationKey The key to use for verifying the signature 47 | @return YES if the provided signature is valid, NO otherwise 48 | */ 49 | - (BOOL)verifySignedInput:(NSString *)input withSignature:(NSString *)signature verificationKey:(NSString *)verificationKey __deprecated_and_will_be_removed_in_release_version(JWTVersion_3_0_0); 50 | 51 | @optional 52 | 53 | /** 54 | Encodes and encrypts the provided payload using the provided secret key 55 | @param theStringData The data to encode 56 | @param theSecretData The secret data to use for encryption 57 | @return An NSData object containing the encrypted payload, or nil if something went wrong. 58 | */ 59 | - (NSData *)encodePayloadData:(NSData *)theStringData withSecret:(NSData *)theSecretData; 60 | 61 | /** 62 | Verifies the provided signature using the signed input and verification key (as data) 63 | @param input The header and payload encoded string 64 | @param signature The JWT provided signature 65 | @param verificationKeyData The key data to use for verifying the signature 66 | @return YES if the provided signature is valid, NO otherwise 67 | */ 68 | - (BOOL)verifySignedInput:(NSString *)input withSignature:(NSString *)signature verificationKeyData:(NSData *)verificationKeyData; 69 | @end 70 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmFactory.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 07.10.15. 6 | // Copyright © 2015 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JWTAlgorithm.h" 11 | @interface JWTAlgorithmFactory : NSObject 12 | 13 | + (NSArray *)algorithms; 14 | + (id)algorithmByName:(NSString *)name; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmFactory.m 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 07.10.15. 6 | // Copyright © 2015 Karma. All rights reserved. 7 | // 8 | 9 | #import "JWTAlgorithmFactory.h" 10 | #import "JWTAlgorithmHSBase.h" 11 | #import "JWTAlgorithmRSBase.h" 12 | #import "JWTAlgorithmNone.h" 13 | 14 | // not implemented. 15 | NSString *const JWTAlgorithmNameES256 = @"ES256"; 16 | NSString *const JWTAlgorithmNameES384 = @"ES384"; 17 | NSString *const JWTAlgorithmNameES512 = @"ES512"; 18 | 19 | @implementation JWTAlgorithmFactory 20 | 21 | + (NSArray *)algorithms { 22 | return @[ 23 | [JWTAlgorithmNone new], 24 | [JWTAlgorithmHSBase algorithm256], 25 | [JWTAlgorithmHSBase algorithm384], 26 | [JWTAlgorithmHSBase algorithm512], 27 | [JWTAlgorithmRSBase algorithm256], 28 | [JWTAlgorithmRSBase algorithm384], 29 | [JWTAlgorithmRSBase algorithm512] 30 | ]; 31 | 32 | } 33 | 34 | + (id)algorithmByName:(NSString *)name { 35 | id algorithm = nil; 36 | 37 | NSString *algName = [name copy]; 38 | 39 | NSUInteger index = [[self algorithms] indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { 40 | // lowercase comparison 41 | return [obj.name.lowercaseString isEqualToString:algName.lowercaseString]; 42 | }]; 43 | 44 | if (index != NSNotFound) { 45 | algorithm = [self algorithms][index]; 46 | } 47 | 48 | return algorithm; 49 | } 50 | 51 | @end -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmNone.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 16.10.15. 6 | // Copyright © 2015 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JWTAlgorithm.h" 11 | extern NSString *const JWTAlgorithmNameNone; 12 | 13 | @interface JWTAlgorithmNone : NSObject 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmNone.m 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 16.10.15. 6 | // Copyright © 2015 Karma. All rights reserved. 7 | // 8 | 9 | #import "JWTAlgorithmNone.h" 10 | NSString *const JWTAlgorithmNameNone = @"none"; 11 | 12 | @implementation JWTAlgorithmNone 13 | 14 | - (NSString *)name { 15 | return JWTAlgorithmNameNone; 16 | } 17 | 18 | - (NSData *)signHash:(NSData *)hash key:(NSData *)key error:(NSError *__autoreleasing *)error { 19 | return [NSData data]; 20 | } 21 | 22 | - (BOOL)verifyHash:(NSData *)hash signature:(NSData *)signature key:(NSData *)key error:(NSError *__autoreleasing *)error { 23 | //if a secret is provided, this isn't the None algorithm 24 | if (key && key.length > 0) { 25 | return NO; 26 | } 27 | 28 | //If the signature isn't blank, this isn't the None algorithm 29 | if (signature && signature.length > 0) { 30 | return NO; 31 | } 32 | 33 | return YES; 34 | } 35 | 36 | - (NSData *)encodePayload:(NSString *)theString withSecret:(NSString *)theSecret { 37 | return [self encodePayloadData:[theSecret dataUsingEncoding:NSUTF8StringEncoding] withSecret:[theSecret dataUsingEncoding:NSUTF8StringEncoding]]; 38 | } 39 | 40 | - (NSData *)encodePayloadData:(NSData *)theStringData withSecret:(NSData *)theSecretData 41 | { 42 | return [self signHash:theStringData key:theSecretData error:nil]; 43 | } 44 | 45 | - (BOOL)verifySignedInput:(NSString *)input withSignature:(NSString *)signature verificationKey:(NSString *)verificationKey 46 | { 47 | return [self verifySignedInput:input withSignature:signature verificationKeyData:verificationKey]; 48 | } 49 | 50 | - (BOOL)verifySignedInput:(NSString *)input withSignature:(NSString *)signature verificationKeyData:(NSData *)verificationKeyData 51 | { 52 | return [self verifyHash:[input dataUsingEncoding:NSUTF8StringEncoding] signature:[signature dataUsingEncoding:NSUTF8StringEncoding] key:verificationKeyData error:nil]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmESBase.h 3 | // Pods 4 | // 5 | // Created by Lobanov Dmitry on 12.02.17. 6 | // 7 | // 8 | 9 | #import 10 | #import "JWTRSAlgorithm.h" 11 | extern NSString *const JWTAlgorithmNameES256; 12 | extern NSString *const JWTAlgorithmNameES384; 13 | extern NSString *const JWTAlgorithmNameES512; 14 | @interface JWTAlgorithmESBase : NSObject @end 15 | 16 | @interface JWTAlgorithmESBase (JWTAsymmetricKeysAlgorithm) @end 17 | 18 | @interface JWTAlgorithmESBase (Create) 19 | 20 | + (instancetype)algorithm256; 21 | + (instancetype)algorithm384; 22 | + (instancetype)algorithm512; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmESBase.m 3 | // Pods 4 | // 5 | // Created by Lobanov Dmitry on 12.02.17. 6 | // 7 | // 8 | 9 | #import "JWTAlgorithmESBase.h" 10 | #import 11 | @interface JWTAlgorithmESBase () 12 | 13 | @end 14 | @implementation JWTAlgorithmESBase 15 | @synthesize keyExtractorType; 16 | @synthesize signKey; 17 | @synthesize verifyKey; 18 | @end 19 | 20 | // thanks! https://github.com/soyersoyer/SwCrypt 21 | @interface JWTAlgorithmESBase (ImportKeys) 22 | - (void)importKey; 23 | //importKey(publicKey, format: .importKeyBinary, keyType: .keyPublic) 24 | @end 25 | @implementation JWTAlgorithmESBase (ImportKeys) 26 | - (void)importKey { 27 | return; 28 | } 29 | @end 30 | 31 | @implementation JWTAlgorithmESBase (JWTAsymmetricKeysAlgorithm) 32 | - (NSString *)name { 33 | return @"ESBase"; 34 | } 35 | - (NSData *)signHash:(NSData *)hash key:(NSData *)key error:(NSError *__autoreleasing *)error { 36 | return nil; 37 | } 38 | - (BOOL)verifyHash:(NSData *)hash signature:(NSData *)signature key:(NSData *)key error:(NSError *__autoreleasing *)error { 39 | return NO; 40 | } 41 | @end 42 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmHSBase.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.03.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JWTAlgorithm.h" 11 | extern NSString *const JWTAlgorithmNameHS256; 12 | extern NSString *const JWTAlgorithmNameHS384; 13 | extern NSString *const JWTAlgorithmNameHS512; 14 | 15 | @interface JWTAlgorithmHSBase : NSObject 16 | 17 | @property (assign, nonatomic, readonly) size_t ccSHANumberDigestLength; 18 | @property (assign, nonatomic, readonly) uint32_t ccHmacAlgSHANumber; 19 | 20 | @end 21 | 22 | @interface JWTAlgorithmHSBase (Create) 23 | 24 | + (instancetype)algorithm256; 25 | + (instancetype)algorithm384; 26 | + (instancetype)algorithm512; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmDataHolderChain.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 02.10.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JWTAlgorithmDataHolder.h" 11 | 12 | @interface JWTAlgorithmDataHolderChain : NSObject 13 | 14 | @property (strong, nonatomic, readonly) NSArray *holders; 15 | 16 | #pragma mark - Initialization 17 | - (instancetype)initWithHolders:(NSArray *)holders; 18 | - (instancetype)initWithHolder:(id)holder; 19 | 20 | #pragma mark - Appending 21 | - (instancetype)chainByAppendingChain:(JWTAlgorithmDataHolderChain *)chain; 22 | - (instancetype)chainByAppendingHolders:(NSArray *)holders; 23 | - (instancetype)chainByAppendingHolder:(id)holder; 24 | 25 | #pragma mark - Create 26 | + (instancetype)chainWithHolders:(NSArray *)holders; 27 | + (instancetype)chainWithHolder:(id)holder; 28 | @end 29 | 30 | @interface JWTAlgorithmDataHolderChain (HoldersPopulation) 31 | - (NSArray *)singleAlgorithm:(id)algorithm withManySecretData:(NSArray *)secretsData; 32 | - (NSArray *)singleSecretData:(NSData *)secretData withManyAlgorithms:(NSArray *)algorithms; 33 | 34 | - (instancetype)chainByPopulatingAlgorithm:(id)algorithm withManySecretData:(NSArray *)secretsData; 35 | - (instancetype)chainByPopulatingSecretData:(NSData *)secretData withManyAlgorithms:(NSArray *)algorithms; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTAlgorithmRSBase.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.03.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JWTRSAlgorithm.h" 11 | extern NSString *const JWTAlgorithmNameRS256; 12 | extern NSString *const JWTAlgorithmNameRS384; 13 | extern NSString *const JWTAlgorithmNameRS512; 14 | 15 | @interface JWTAlgorithmRSBase : NSObject 16 | 17 | @property (assign, nonatomic, readonly) size_t ccSHANumberDigestLength; 18 | @property (assign, nonatomic, readonly) uint32_t secPaddingPKCS1SHANumber; 19 | - (unsigned char *)CC_SHANumberWithData:(const void *)data withLength:(uint32_t)len withHashBytes:(unsigned char *)hashBytes; 20 | 21 | @end 22 | 23 | @interface JWTAlgorithmRSBase (Create) 24 | 25 | + (instancetype)algorithm256; 26 | + (instancetype)algorithm384; 27 | + (instancetype)algorithm512; 28 | + (instancetype)mutableAlgorithm __deprecated; 29 | 30 | @end 31 | 32 | /* 33 | // when you can't live without mutability, uncomment. 34 | @class JWTAlgorithmRSFamilyMemberMutable; 35 | */ -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTRSAlgorithm.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Marcelo Schroeder on 12/03/2016. 3 | // Copyright (c) 2016 Karma. All rights reserved. 4 | // 5 | 6 | #import 7 | #import "JWTAlgorithm.h" 8 | @protocol JWTCryptoKeyProtocol; 9 | 10 | @protocol JWTAsymmetricKeysAlgorithm 11 | 12 | @optional 13 | @property(nonatomic, readwrite, copy) NSString *keyExtractorType; 14 | @property(nonatomic, readwrite, strong) id signKey; 15 | @property(nonatomic, readwrite, strong) id verifyKey; 16 | 17 | @end 18 | 19 | @protocol JWTRSAlgorithm 20 | 21 | @required 22 | @property(nonatomic, readwrite, copy) NSString *privateKeyCertificatePassphrase; 23 | @end 24 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCryptoKey.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 04.02.17. 6 | // Copyright © 2017 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol JWTCryptoKeyProtocol 13 | @property (copy, nonatomic, readonly) NSString *tag; 14 | @property (assign, nonatomic, readonly) SecKeyRef key; 15 | @property (copy, nonatomic, readonly) NSData *rawKey; 16 | @end 17 | 18 | @interface JWTCryptoKeyBuilder : NSObject 19 | @property (assign, nonatomic, readonly) NSString *keyType; 20 | - (instancetype)keyTypeRSA; 21 | - (instancetype)keyTypeEC; 22 | @end 23 | 24 | @interface JWTCryptoKey : NSObject 25 | - (instancetype)initWithData:(NSData *)data parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; //NS_DESIGNATED_INITIALIZER 26 | - (instancetype)initWithBase64String:(NSString *)base64String parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 27 | - (instancetype)initWithPemEncoded:(NSString *)encoded parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 28 | - (instancetype)initWithPemAtURL:(NSURL *)url parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 29 | @end 30 | 31 | @interface JWTCryptoKey (Parameters) 32 | + (NSString *)parametersKeyBuilder; 33 | @end 34 | 35 | @interface JWTCryptoKeyPublic : JWTCryptoKey 36 | - (instancetype)initWithCertificateData:(NSData *)certificateData parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; //NS_DESIGNATED_INITIALIZER; 37 | - (instancetype)initWithCertificateBase64String:(NSString *)certificateString parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 38 | @end 39 | 40 | @interface JWTCryptoKeyPrivate : JWTCryptoKey 41 | - (instancetype)initWithP12Data:(NSData *)p12Data withPassphrase:(NSString *)passphrase parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; //NS_DESIGNATED_INITIALIZER; 42 | - (instancetype)initWithP12AtURL:(NSURL *)url withPassphrase:(NSString *)passphrase parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 43 | @end 44 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCryptoKeyExtractor.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 04.02.17. 6 | // Copyright © 2017 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @protocol JWTCryptoKeyProtocol; 13 | @protocol JWTCryptoKeyExtractorProtocol 14 | @optional 15 | - (id)keyFromString:(NSString *)string parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 16 | - (id)keyFromData:(NSData *)data parameters:(NSDictionary *)parameters error:(NSError *__autoreleasing*)error; 17 | @end 18 | 19 | @interface JWTCryptoKeyExtractor : NSObject 20 | @property (copy, nonatomic, readonly) NSString *type; 21 | + (NSString *)type; 22 | + (NSString *)parametersKeyCertificatePassphrase; 23 | @end 24 | 25 | @interface JWTCryptoKeyExtractor (ClassCluster) 26 | + (instancetype)publicKeyWithCertificate; 27 | + (instancetype)privateKeyInP12; 28 | + (instancetype)publicKeyWithPEMBase64; 29 | + (instancetype)privateKeyWithPEMBase64; 30 | + (instancetype)createWithType:(NSString *)type; 31 | @end 32 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCryptoSecurity.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 04.02.17. 6 | // Copyright © 2017 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | // Thanks for https://github.com/TakeScoop/SwiftyRSA! 12 | @interface JWTCryptoSecurity : NSObject 13 | + (NSString *)keyTypeRSA; 14 | + (NSString *)keyTypeEC; 15 | + (SecKeyRef)addKeyWithData:(NSData *)data asPublic:(BOOL)public tag:(NSString *)tag type:(NSString *)type error:(NSError *__autoreleasing*)error; 16 | + (SecKeyRef)addKeyWithData:(NSData *)data asPublic:(BOOL)public tag:(NSString *)tag error:(NSError *__autoreleasing*)error; 17 | + (SecKeyRef)keyByTag:(NSString *)tag error:(NSError *__autoreleasing*)error; 18 | + (void)removeKeyByTag:(NSString *)tag error:(NSError *__autoreleasing*)error; 19 | @end 20 | 21 | @interface JWTCryptoSecurity (Certificates) 22 | + (OSStatus)extractIdentityAndTrustFromPKCS12:(CFDataRef)inPKCS12Data password:(CFStringRef)password identity:(SecIdentityRef *)outIdentity trust:(SecTrustRef *)outTrust; 23 | + (SecKeyRef)publicKeyFromCertificate:(NSData *)certificateData; 24 | @end 25 | 26 | @interface JWTCryptoSecurity (Pem) 27 | + (NSString *)certificateFromPemFileContent:(NSString *)content; 28 | + (NSString *)keyFromPemFileContent:(NSString *)content; 29 | + (NSArray *)itemsFromPemFileContent:(NSString *)content byRegex:(NSRegularExpression *)expression; 30 | + (NSString *)certificateFromPemFileWithName:(NSString *)name; 31 | + (NSString *)keyFromPemFileWithName:(NSString *)name; 32 | + (NSArray *)itemsFromPemFileWithName:(NSString *)name byRegex:(NSRegularExpression *)expression; 33 | + (NSString *)stringByRemovingPemHeadersFromString:(NSString *)string; 34 | @end 35 | 36 | @interface JWTCryptoSecurity (PublicKey) 37 | + (NSData *)dataByRemovingPublicKeyHeader:(NSData *)data error:(NSError *__autoreleasing*)error; 38 | @end 39 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/ClaimSet/JWTClaim.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaim.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.02.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JWTClaim : NSObject 12 | 13 | + (NSString *)name; 14 | + (instancetype)claimByName:(NSString *)name; 15 | + (BOOL)verifyValue:(NSObject *)value withTrustedValue:(NSObject *)trustedValue; 16 | - (BOOL)verifyValue:(NSObject *)value withTrustedValue:(NSObject *)trustedValue; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSet.h 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface JWTClaimsSet : NSObject 12 | 13 | @property (nonatomic, readwrite, copy) NSString *issuer; 14 | @property (nonatomic, readwrite, copy) NSString *subject; 15 | @property (nonatomic, readwrite, copy) NSString *audience; 16 | @property (nonatomic, readwrite, copy) NSDate *expirationDate; 17 | @property (nonatomic, readwrite, copy) NSDate *notBeforeDate; 18 | @property (nonatomic, readwrite, copy) NSDate *issuedAt; 19 | @property (nonatomic, readwrite, copy) NSString *identifier; 20 | @property (nonatomic, readwrite, copy) NSString *type; 21 | @property (nonatomic, readwrite, copy) NSString *scope; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSet.m 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import "JWTClaimsSet.h" 10 | 11 | @implementation JWTClaimsSet 12 | 13 | - (id)copyWithZone:(NSZone *)zone { 14 | JWTClaimsSet *newClaimsSet = [[self.class alloc] init]; 15 | 16 | newClaimsSet.issuer = self.issuer; 17 | newClaimsSet.subject = self.subject; 18 | newClaimsSet.audience = self.audience; 19 | newClaimsSet.expirationDate = self.expirationDate; 20 | newClaimsSet.notBeforeDate = self.notBeforeDate; 21 | newClaimsSet.issuedAt = self.issuedAt; 22 | newClaimsSet.identifier = self.identifier; 23 | newClaimsSet.type = self.type; 24 | newClaimsSet.scope = self.scope; 25 | 26 | return newClaimsSet; 27 | } 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSetSerializer.h 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "JWTClaimsSet.h" 12 | 13 | @interface JWTClaimsSetSerializer : NSObject 14 | 15 | + (NSArray *)claimsSetKeys; 16 | + (NSDictionary *)dictionaryWithClaimsSet:(JWTClaimsSet *)theClaimsSet; 17 | + (JWTClaimsSet *)claimsSetWithDictionary:(NSDictionary *)theDictionary; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSetSerializer.m 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import "JWTClaimsSetSerializer.h" 10 | 11 | @implementation JWTClaimsSetSerializer 12 | 13 | + (NSArray *)claimsSetKeys 14 | { 15 | return @[@"iss", @"sub", @"aud", @"exp", @"nbf", @"iat", @"jti", @"typ", @"scope"]; 16 | } 17 | 18 | + (NSDictionary *)dictionaryWithClaimsSet:(JWTClaimsSet *)theClaimsSet; 19 | { 20 | NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 21 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.issuer forKey:@"iss"]; 22 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.subject forKey:@"sub"]; 23 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.audience forKey:@"aud"]; 24 | [self dictionary:dictionary setDateIfNotNil:theClaimsSet.expirationDate forKey:@"exp"]; 25 | [self dictionary:dictionary setDateIfNotNil:theClaimsSet.notBeforeDate forKey:@"nbf"]; 26 | [self dictionary:dictionary setDateIfNotNil:theClaimsSet.issuedAt forKey:@"iat"]; 27 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.identifier forKey:@"jti"]; 28 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.type forKey:@"typ"]; 29 | [self dictionary:dictionary setObjectIfNotNil:theClaimsSet.scope forKey:@"scope"]; 30 | 31 | return [dictionary copy]; 32 | } 33 | 34 | + (JWTClaimsSet *)claimsSetWithDictionary:(NSDictionary *)theDictionary; 35 | { 36 | JWTClaimsSet *claimsSet = [[JWTClaimsSet alloc] init]; 37 | claimsSet.issuer = [theDictionary objectForKey:@"iss"]; 38 | claimsSet.subject = [theDictionary objectForKey:@"sub"]; 39 | claimsSet.audience = [theDictionary objectForKey:@"aud"]; 40 | claimsSet.expirationDate = [NSDate dateWithTimeIntervalSince1970:[[theDictionary objectForKey:@"exp"] doubleValue]]; 41 | claimsSet.notBeforeDate = [NSDate dateWithTimeIntervalSince1970:[[theDictionary objectForKey:@"nbf"] doubleValue]]; 42 | claimsSet.issuedAt = [NSDate dateWithTimeIntervalSince1970:[[theDictionary objectForKey:@"iat"] doubleValue]]; 43 | claimsSet.identifier = [theDictionary objectForKey:@"jti"]; 44 | claimsSet.type = [theDictionary objectForKey:@"typ"]; 45 | claimsSet.scope = [theDictionary objectForKey:@"scope"]; 46 | 47 | return claimsSet; 48 | } 49 | 50 | + (void)dictionary:(NSMutableDictionary *)theDictionary setObjectIfNotNil:(id)theObject forKey:(id)theKey; 51 | { 52 | if (!theObject) 53 | return; 54 | 55 | [theDictionary setObject:theObject forKey:theKey]; 56 | } 57 | 58 | + (void)dictionary:(NSMutableDictionary *)theDictionary setDateIfNotNil:(NSDate*)date forKey:(id)theKey; 59 | { 60 | if (!date) 61 | return; 62 | NSNumber* value = @((unsigned long)[date timeIntervalSince1970]); 63 | 64 | [theDictionary setObject:value forKey:theKey]; 65 | } 66 | 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSetVerifier.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.02.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JWTClaimsSet.h" 11 | 12 | @interface JWTClaimsSetVerifier : NSObject 13 | 14 | + (BOOL)verifyClaimsSet:(JWTClaimsSet *)theClaimsSet withTrustedClaimsSet:(JWTClaimsSet *)trustedClaimsSet; 15 | 16 | + (BOOL)verifyClaimsSetDictionary:(NSDictionary *)theClaimsSetDictionary withTrustedClaimsSet:(JWTClaimsSet *)trustedClaimsSet; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTClaimsSetVerifier.m 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 13.02.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import "JWTClaimsSetVerifier.h" 10 | #import "JWTClaimsSetSerializer.h" 11 | #import "JWTClaim.h" 12 | 13 | @implementation JWTClaimsSetVerifier 14 | 15 | + (BOOL)verifyDictionary:(NSDictionary *)dictionary withTrustedDictionary:(NSDictionary *)trustedDictionary byKey:(NSString *)key { 16 | NSObject *value = dictionary[key]; 17 | NSObject *trustedValue = trustedDictionary[key]; 18 | 19 | BOOL result = YES; 20 | 21 | if (trustedValue) { 22 | result = [[JWTClaim claimByName:key] verifyValue:value withTrustedValue:trustedValue]; 23 | } 24 | 25 | return result; 26 | } 27 | 28 | + (BOOL)verifyClaimsSetDictionary:(NSDictionary *)theClaimsSetDictionary withTrustedClaimsSetDictionary:(NSDictionary *)trustedClaimsSetDictionary { 29 | 30 | NSArray *claimsSets = [JWTClaimsSetSerializer claimsSetKeys]; 31 | 32 | if (!trustedClaimsSetDictionary) { 33 | return YES; 34 | } 35 | 36 | if (!theClaimsSetDictionary) { 37 | return NO; 38 | } 39 | 40 | BOOL result = YES; 41 | 42 | for (NSString *key in claimsSets) { 43 | result = result && [self verifyDictionary:theClaimsSetDictionary withTrustedDictionary:trustedClaimsSetDictionary byKey:key]; 44 | } 45 | 46 | return result; 47 | } 48 | 49 | 50 | + (BOOL)verifyClaimsSet:(JWTClaimsSet *)theClaimsSet withTrustedClaimsSet:(JWTClaimsSet *)trustedClaimsSet { 51 | 52 | NSDictionary *dictionary = [JWTClaimsSetSerializer dictionaryWithClaimsSet:theClaimsSet]; 53 | 54 | NSDictionary *trustedDictionary = [JWTClaimsSetSerializer dictionaryWithClaimsSet:trustedClaimsSet]; 55 | 56 | NSArray *claimsSets = [JWTClaimsSetSerializer claimsSetKeys]; 57 | 58 | BOOL result = YES; 59 | for (NSString *key in claimsSets) { 60 | result = result && [self verifyDictionary:dictionary withTrustedDictionary:trustedDictionary byKey:key]; 61 | } 62 | 63 | return result; 64 | } 65 | 66 | + (BOOL)verifyClaimsSetDictionary:(NSDictionary *)theClaimsSetDictionary withTrustedClaimsSet:(JWTClaimsSet *)trustedClaimsSet { 67 | NSDictionary *trustedDictionary = [JWTClaimsSetSerializer dictionaryWithClaimsSet:trustedClaimsSet]; 68 | 69 | return [self verifyClaimsSetDictionary:theClaimsSetDictionary withTrustedClaimsSetDictionary:trustedDictionary]; 70 | } 71 | 72 | @end -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTCoding+ResultTypes.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 30.11.16. 6 | // Copyright © 2016 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | @class JWTClaimsSet; 11 | 12 | extern NSString *JWTCodingResultHeaders; 13 | extern NSString *JWTCodingResultPayload; 14 | 15 | @interface JWT (ResultTypes) @end 16 | 17 | /* 18 | ResultType 19 | /\ 20 | / \ 21 | / \ 22 | Success Error 23 | 24 | Protocols: Mutable and Immutable (?!?) 25 | */ 26 | 27 | // Public 28 | @protocol JWTCodingResultTypeSuccessEncodedProtocol 29 | @property (copy, nonatomic, readonly) NSString *encoded; 30 | - (instancetype)initWithEncoded:(NSString *)encoded; 31 | @property (copy, nonatomic, readonly) NSString *token; 32 | - (instancetype)initWithToken:(NSString *)token; 33 | @end 34 | 35 | // Public 36 | @protocol JWTCodingResultTypeSuccessDecodedProtocol 37 | @property (copy, nonatomic, readonly) NSDictionary *headers; 38 | @property (copy, nonatomic, readonly) NSDictionary *payload; 39 | 40 | // dictionary @{ 41 | // JWTCodingResultHeaders : self.headers, 42 | // JWTCodingResultPayload : self.payload 43 | //} 44 | @property (copy, nonatomic, readonly) NSDictionary *headerAndPayloadDictionary; 45 | 46 | @property (nonatomic, readonly) JWTClaimsSet *claimsSet; 47 | - (instancetype)initWithHeaders:(NSDictionary *)headers withPayload:(NSDictionary *)payload; 48 | - (instancetype)initWithClaimsSet:(JWTClaimsSet *)claimsSet; 49 | @end 50 | 51 | // Public 52 | @interface JWTCodingResultTypeSuccess : NSObject @end 53 | 54 | // Public 55 | @protocol JWTCodingResultTypeErrorProtocol 56 | @property (copy, nonatomic, readonly) NSError *error; 57 | - (instancetype)initWithError:(NSError *)error; 58 | @end 59 | 60 | @interface JWTCodingResultTypeError : NSObject @end 61 | 62 | @interface JWTCodingResultType : NSObject 63 | - (instancetype)initWithSuccessResult:(JWTCodingResultTypeSuccess *)success; 64 | - (instancetype)initWithErrorResult:(JWTCodingResultTypeError *)error; 65 | @property (strong, nonatomic, readonly) JWTCodingResultTypeSuccess *successResult; 66 | @property (strong, nonatomic, readonly) JWTCodingResultTypeError *errorResult; 67 | @end 68 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Coding/JWTCoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWT.h 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | /** 11 | @discussion JWT is a general interface for decoding and encoding. 12 | Now it is to complex and fat to support. 13 | Possible solution: split interface into several pieces. 14 | 15 | JWT_1_0 -> JWT with plain old functions. 16 | JWT_2_0 -> JWT with builder usage. 17 | JWT_3_0 -> JWT with splitted apart algorithm data and payload data. 18 | */ 19 | @interface JWT : NSObject @end 20 | 21 | typedef NS_OPTIONS(NSInteger, JWTCodingDecodingOptions) { 22 | JWTCodingDecodingOptionsNone = 0, 23 | JWTCodingDecodingOptionsSkipVerification = 1 24 | }; 25 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Coding/JWTCoding.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWT.m 3 | // JWT 4 | // 5 | // Created by Klaas Pieter Annema on 31-05-13. 6 | // Copyright (c) 2013 Karma. All rights reserved. 7 | // 8 | 9 | #import "JWTCoding.h" 10 | 11 | @implementation JWT @end 12 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/FrameworkSupplement/JWT.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWT.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 23.10.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for JWT. 12 | FOUNDATION_EXPORT double JWTVersionNumber; 13 | 14 | //! Project version string for JWT. 15 | FOUNDATION_EXPORT const unsigned char JWTVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | // Coding 20 | #import 21 | #import 22 | #import 23 | #import 24 | #import 25 | 26 | // Algorithms 27 | #import 28 | #import 29 | #import 30 | #import 31 | #import 32 | #import 33 | 34 | // Holders 35 | #import 36 | #import 37 | 38 | // Claims 39 | #import 40 | #import 41 | #import 42 | #import 43 | 44 | // Supplement 45 | #import 46 | #import 47 | #import 48 | 49 | // Crypto 50 | #import 51 | #import 52 | #import 53 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/FrameworkSupplement/Map.modulemap: -------------------------------------------------------------------------------- 1 | framework module JWT { 2 | umbrella header "JWT.h" 3 | export * 4 | module * { export * } 5 | } -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTBase64Coder.h 3 | // Pods 4 | // 5 | // Created by Lobanov Dmitry on 05.10.16. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @protocol JWTStringCoder__Protocol 12 | - (NSString *)stringWithData:(NSData *)data; 13 | - (NSData *)dataWithString:(NSString *)string; 14 | @end 15 | 16 | @interface JWTBase64Coder : NSObject 17 | + (NSString *)base64UrlEncodedStringWithData:(NSData *)data; 18 | + (NSData *)dataWithBase64UrlEncodedString:(NSString *)urlEncodedString; 19 | @end 20 | 21 | @interface JWTBase64Coder (JWTStringCoder__Protocol) @end 22 | 23 | 24 | @interface JWTStringCoder__For__Encoding : NSObject 25 | @property (assign, nonatomic, readwrite) NSStringEncoding stringEncoding; 26 | + (instancetype)utf8Encoding; 27 | @end 28 | @interface JWTStringCoder__For__Encoding (JWTStringCoder__Protocol) @end 29 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.m: -------------------------------------------------------------------------------- 1 | // 2 | // JWTBase64Coder.m 3 | // Pods 4 | // 5 | // Created by Lobanov Dmitry on 05.10.16. 6 | // 7 | // 8 | 9 | #import "JWTBase64Coder.h" 10 | 11 | @interface JWTBase64Coder (ConditionLinking) 12 | + (BOOL)isBase64AddtionsAvailable; 13 | @end 14 | 15 | @implementation JWTBase64Coder (ConditionLinking) 16 | + (BOOL)isBase64AddtionsAvailable { 17 | return [[NSData class] respondsToSelector:@selector(dataWithBase64UrlEncodedString:)]; 18 | } 19 | @end 20 | 21 | #if __has_include("MF_Base64Additions.h") 22 | #import 23 | #endif 24 | 25 | @implementation JWTBase64Coder 26 | 27 | + (NSString *)base64UrlEncodedStringWithData:(NSData *)data { 28 | if ([self isBase64AddtionsAvailable] && [data respondsToSelector:@selector(base64UrlEncodedString)]) { 29 | return [data performSelector:@selector(base64UrlEncodedString)]; 30 | } 31 | else { 32 | return [data base64EncodedStringWithOptions:0]; 33 | } 34 | } 35 | 36 | + (NSData *)dataWithBase64UrlEncodedString:(NSString *)urlEncodedString { 37 | if ([self isBase64AddtionsAvailable] && [[NSData class] respondsToSelector:@selector(dataWithBase64UrlEncodedString:)]) { 38 | return [[NSData class] performSelector:@selector(dataWithBase64UrlEncodedString:) withObject:urlEncodedString]; 39 | } 40 | else { 41 | return [[NSData alloc] initWithBase64EncodedString:urlEncodedString options:0]; 42 | } 43 | } 44 | 45 | @end 46 | 47 | @implementation JWTBase64Coder (JWTStringCoder__Protocol) 48 | - (NSString *)stringWithData:(NSData *)data { 49 | return [self.class base64UrlEncodedStringWithData:data]; 50 | } 51 | - (NSData *)dataWithString:(NSString *)string { 52 | return [self.class dataWithBase64UrlEncodedString:string]; 53 | } 54 | @end 55 | 56 | @implementation JWTStringCoder__For__Encoding 57 | + (instancetype)utf8Encoding { 58 | JWTStringCoder__For__Encoding *coding = [self new]; 59 | coding.stringEncoding = NSUTF8StringEncoding; 60 | return coding; 61 | } 62 | @end 63 | @implementation JWTStringCoder__For__Encoding (JWTStringCoder__Protocol) 64 | - (NSString *)stringWithData:(NSData *)data { 65 | return [[NSString alloc] initWithData:data encoding:self.stringEncoding]; 66 | } 67 | - (NSData *)dataWithString:(NSString *)string { 68 | return [string dataUsingEncoding:self.stringEncoding]; 69 | } 70 | @end 71 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Supplement/JWTDeprecations.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTDeprecations.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 31.08.16. 6 | // Copyright © 2016 Karma. All rights reserved. 7 | // 8 | 9 | #ifndef JWTDeprecations_h 10 | #define JWTDeprecations_h 11 | 12 | #define STR(str) #str 13 | #define JWTVersion_2_1_0 2.1 14 | #define JWTVersion_2_2_0 2.2 15 | #define JWTVersion_3_0_0 3.0 16 | #define __first_deprecated_in_release_version(version) __deprecated_msg("first deprecated in release version: " STR(version)) 17 | #define __deprecated_and_will_be_removed_in_release_version(version) __deprecated_msg("deprecated. will be removed in release version: "STR(version)) 18 | #define __available_in_release_version(version) __deprecated_msg("will be introduced in release version: " STR(version)) 19 | 20 | #define __jwt_technical_debt(debt) __deprecated_msg("Don't forget to inspect it later." STR(debt)) 21 | 22 | #endif /* JWTDeprecations_h */ 23 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.h: -------------------------------------------------------------------------------- 1 | // 2 | // JWTErrorDescription.h 3 | // JWT 4 | // 5 | // Created by Lobanov Dmitry on 27.11.16. 6 | // Copyright © 2016 JWTIO. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString *JWTErrorDomain; 12 | 13 | typedef NS_ENUM(NSInteger, JWTError) { 14 | JWTInvalidFormatError = -100, 15 | JWTUnsupportedAlgorithmError, 16 | JWTAlgorithmNameMismatchError, 17 | JWTInvalidSignatureError, 18 | JWTNoPayloadError, 19 | JWTNoHeaderError, 20 | JWTEncodingHeaderError, 21 | JWTEncodingPayloadError, 22 | JWTEncodingSigningError, 23 | JWTClaimsSetVerificationFailed, 24 | JWTInvalidSegmentSerializationError, 25 | JWTUnspecifiedAlgorithmError, 26 | JWTBlacklistedAlgorithmError, 27 | JWTDecodingHeaderError, 28 | JWTDecodingPayloadError, 29 | JWTDecodingHoldersChainEmptyError 30 | }; 31 | 32 | @interface JWTErrorDescription : NSObject 33 | + (NSError *)errorWithCode:(JWTError)code; 34 | @end 35 | -------------------------------------------------------------------------------- /ios/CodePush/JWT/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Karma Mobility, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /ios/CodePush/RCTConvert+CodePushInstallMode.m: -------------------------------------------------------------------------------- 1 | #import "CodePush.h" 2 | 3 | #if __has_include() 4 | #import 5 | #else 6 | #import "RCTConvert.h" 7 | #endif 8 | 9 | // Extending the RCTConvert class allows the React Native 10 | // bridge to handle args of type "CodePushInstallMode" 11 | @implementation RCTConvert (CodePushInstallMode) 12 | 13 | RCT_ENUM_CONVERTER(CodePushInstallMode, (@{ @"codePushInstallModeImmediate": @(CodePushInstallModeImmediate), 14 | @"codePushInstallModeOnNextRestart": @(CodePushInstallModeOnNextRestart), 15 | @"codePushInstallModeOnNextResume": @(CodePushInstallModeOnNextResume), 16 | @"codePushInstallModeOnNextSuspend": @(CodePushInstallModeOnNextSuspend) }), 17 | CodePushInstallModeImmediate, // Default enum value 18 | integerValue) 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ios/CodePush/RCTConvert+CodePushUpdateState.m: -------------------------------------------------------------------------------- 1 | #import "CodePush.h" 2 | 3 | #if __has_include() 4 | #import 5 | #else 6 | #import "RCTConvert.h" 7 | #endif 8 | 9 | // Extending the RCTConvert class allows the React Native 10 | // bridge to handle args of type "CodePushUpdateState" 11 | @implementation RCTConvert (CodePushUpdateState) 12 | 13 | RCT_ENUM_CONVERTER(CodePushUpdateState, (@{ @"codePushUpdateStateRunning": @(CodePushUpdateStateRunning), 14 | @"codePushUpdateStatePending": @(CodePushUpdateStatePending), 15 | @"codePushUpdateStateLatest": @(CodePushUpdateStateLatest) 16 | }), 17 | CodePushUpdateStateRunning, // Default enum value 18 | integerValue) 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ios/CodePush/SSZipArchive/README.md: -------------------------------------------------------------------------------- 1 | The source code in this folder is taken from [https://github.com/ZipArchive/ZipArchive/tree/35fe9b6af48527cde0b5db52287474ed3a32d75f/SSZipArchive](https://github.com/ZipArchive/ZipArchive/tree/35fe9b6af48527cde0b5db52287474ed3a32d75f/SSZipArchive) which is [MIT licensed](https://github.com/ZipArchive/ZipArchive/blob/35fe9b6af48527cde0b5db52287474ed3a32d75f/LICENSE.txt). -------------------------------------------------------------------------------- /ios/CodePush/SSZipArchive/aes/entropy.c: -------------------------------------------------------------------------------- 1 | #ifdef _WIN32 2 | #include 3 | #else 4 | #include 5 | #include 6 | #include 7 | #endif 8 | 9 | #if defined(__cplusplus) 10 | extern "C" 11 | { 12 | #endif 13 | 14 | #ifdef _WIN32 15 | int entropy_fun(unsigned char buf[], unsigned int len) 16 | { 17 | HCRYPTPROV provider; 18 | unsigned __int64 pentium_tsc[1]; 19 | unsigned int i; 20 | int result = 0; 21 | 22 | 23 | if (CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) 24 | { 25 | result = CryptGenRandom(provider, len, buf); 26 | CryptReleaseContext(provider, 0); 27 | if (result) 28 | return len; 29 | } 30 | 31 | QueryPerformanceCounter((LARGE_INTEGER *)pentium_tsc); 32 | 33 | for(i = 0; i < 8 && i < len; ++i) 34 | buf[i] = ((unsigned char*)pentium_tsc)[i]; 35 | 36 | return i; 37 | } 38 | #else 39 | int entropy_fun(unsigned char buf[], unsigned int len) 40 | { 41 | int frand = open("/dev/random", O_RDONLY); 42 | int rlen = 0; 43 | if (frand != -1) 44 | { 45 | rlen = (int)read(frand, buf, len); 46 | close(frand); 47 | } 48 | return rlen; 49 | } 50 | #endif 51 | 52 | #if defined(__cplusplus) 53 | } 54 | #endif 55 | -------------------------------------------------------------------------------- /ios/CodePush/SSZipArchive/aes/entropy.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _ENTROPY_FUN_H 3 | #define _ENTROPY_FUN_H 4 | 5 | #if defined(__cplusplus) 6 | extern "C" 7 | { 8 | #endif 9 | 10 | int entropy_fun(unsigned char buf[], unsigned int len); 11 | 12 | #if defined(__cplusplus) 13 | } 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /ios/CodePush/SSZipArchive/aes/pwd2key.h: -------------------------------------------------------------------------------- 1 | /* 2 | --------------------------------------------------------------------------- 3 | Copyright (c) 2002, Dr Brian Gladman, Worcester, UK. All rights reserved. 4 | 5 | LICENSE TERMS 6 | 7 | The free distribution and use of this software in both source and binary 8 | form is allowed (with or without changes) provided that: 9 | 10 | 1. distributions of this source code include the above copyright 11 | notice, this list of conditions and the following disclaimer; 12 | 13 | 2. distributions in binary form include the above copyright 14 | notice, this list of conditions and the following disclaimer 15 | in the documentation and/or other associated materials; 16 | 17 | 3. the copyright holder's name is not used to endorse products 18 | built using this software without specific written permission. 19 | 20 | ALTERNATIVELY, provided that this notice is retained in full, this product 21 | may be distributed under the terms of the GNU General Public License (GPL), 22 | in which case the provisions of the GPL apply INSTEAD OF those given above. 23 | 24 | DISCLAIMER 25 | 26 | This software is provided 'as is' with no explicit or implied warranties 27 | in respect of its properties, including, but not limited to, correctness 28 | and/or fitness for purpose. 29 | --------------------------------------------------------------------------- 30 | Issue Date: 26/08/2003 31 | 32 | This is an implementation of RFC2898, which specifies key derivation from 33 | a password and a salt value. 34 | */ 35 | 36 | #ifndef PWD2KEY_H 37 | #define PWD2KEY_H 38 | 39 | #if defined(__cplusplus) 40 | extern "C" 41 | { 42 | #endif 43 | 44 | void derive_key( 45 | const unsigned char pwd[], /* the PASSWORD, and */ 46 | unsigned int pwd_len, /* its length */ 47 | const unsigned char salt[], /* the SALT and its */ 48 | unsigned int salt_len, /* length */ 49 | unsigned int iter, /* the number of iterations */ 50 | unsigned char key[], /* space for the output key */ 51 | unsigned int key_len); /* and its required length */ 52 | 53 | #if defined(__cplusplus) 54 | } 55 | #endif 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /ios/CodePush/SSZipArchive/aes/sha1.h: -------------------------------------------------------------------------------- 1 | /* 2 | --------------------------------------------------------------------------- 3 | Copyright (c) 2002, Dr Brian Gladman, Worcester, UK. All rights reserved. 4 | 5 | LICENSE TERMS 6 | 7 | The free distribution and use of this software in both source and binary 8 | form is allowed (with or without changes) provided that: 9 | 10 | 1. distributions of this source code include the above copyright 11 | notice, this list of conditions and the following disclaimer; 12 | 13 | 2. distributions in binary form include the above copyright 14 | notice, this list of conditions and the following disclaimer 15 | in the documentation and/or other associated materials; 16 | 17 | 3. the copyright holder's name is not used to endorse products 18 | built using this software without specific written permission. 19 | 20 | ALTERNATIVELY, provided that this notice is retained in full, this product 21 | may be distributed under the terms of the GNU General Public License (GPL), 22 | in which case the provisions of the GPL apply INSTEAD OF those given above. 23 | 24 | DISCLAIMER 25 | 26 | This software is provided 'as is' with no explicit or implied warranties 27 | in respect of its properties, including, but not limited to, correctness 28 | and/or fitness for purpose. 29 | --------------------------------------------------------------------------- 30 | Issue Date: 01/08/2005 31 | */ 32 | 33 | #ifndef _SHA1_H 34 | #define _SHA1_H 35 | 36 | #include 37 | #include "brg_types.h" 38 | 39 | #define SHA1_BLOCK_SIZE 64 40 | #define SHA1_DIGEST_SIZE 20 41 | 42 | #if defined(__cplusplus) 43 | extern "C" 44 | { 45 | #endif 46 | 47 | /* type to hold the SHA256 context */ 48 | 49 | typedef struct 50 | { uint_32t count[2]; 51 | uint_32t hash[5]; 52 | uint_32t wbuf[16]; 53 | } sha1_ctx; 54 | 55 | /* Note that these prototypes are the same for both bit and */ 56 | /* byte oriented implementations. However the length fields */ 57 | /* are in bytes or bits as appropriate for the version used */ 58 | /* and bit sequences are input as arrays of bytes in which */ 59 | /* bit sequences run from the most to the least significant */ 60 | /* end of each byte */ 61 | 62 | VOID_RETURN sha1_compile(sha1_ctx ctx[1]); 63 | 64 | VOID_RETURN sha1_begin(sha1_ctx ctx[1]); 65 | VOID_RETURN sha1_hash(const unsigned char data[], unsigned long len, sha1_ctx ctx[1]); 66 | VOID_RETURN sha1_end(unsigned char hval[], sha1_ctx ctx[1]); 67 | VOID_RETURN sha1(unsigned char hval[], const unsigned char data[], unsigned long len); 68 | 69 | #if defined(__cplusplus) 70 | } 71 | #endif 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /ios/CodePush/SSZipArchive/minizip/mztools.h: -------------------------------------------------------------------------------- 1 | /* 2 | Additional tools for Minizip 3 | Code: Xavier Roche '2004 4 | License: Same as ZLIB (www.gzip.org) 5 | */ 6 | 7 | #ifndef _zip_tools_H 8 | #define _zip_tools_H 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | #ifndef _ZLIB_H 15 | #include "zlib.h" 16 | #endif 17 | 18 | #include "unzip.h" 19 | 20 | /* Repair a ZIP file (missing central directory) 21 | file: file to recover 22 | fileOut: output file after recovery 23 | fileOutTmp: temporary file name used for recovery 24 | */ 25 | extern int ZEXPORT unzRepair(const char* file, 26 | const char* fileOut, 27 | const char* fileOutTmp, 28 | uLong* nRecovered, 29 | uLong* bytesRecovered); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /logging.js: -------------------------------------------------------------------------------- 1 | /* Logs messages to console with the [CodePush] prefix */ 2 | function log(message) { 3 | console.log(`[CodePush] ${message}`); 4 | } 5 | 6 | module.exports = log; 7 | -------------------------------------------------------------------------------- /package-mixins.js: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter } from "react-native"; 2 | import log from "./logging"; 3 | 4 | // This function is used to augment remote and local 5 | // package objects with additional functionality/properties 6 | // beyond what is included in the metadata sent by the server. 7 | module.exports = (NativeCodePush) => { 8 | const remote = () => { 9 | return { 10 | async download(downloadProgressCallback) { 11 | if (!this.downloadUrl) { 12 | throw new Error("Cannot download an update without a download url"); 13 | } 14 | 15 | let downloadProgressSubscription; 16 | if (downloadProgressCallback) { 17 | const codePushEventEmitter = new NativeEventEmitter(NativeCodePush); 18 | // Use event subscription to obtain download progress. 19 | downloadProgressSubscription = codePushEventEmitter.addListener( 20 | "CodePushDownloadProgress", 21 | downloadProgressCallback 22 | ); 23 | } 24 | 25 | // Use the downloaded package info. Native code will save the package info 26 | // so that the client knows what the current package version is. 27 | try { 28 | const updatePackageCopy = Object.assign({}, this); 29 | Object.keys(updatePackageCopy).forEach((key) => (typeof updatePackageCopy[key] === 'function') && delete updatePackageCopy[key]); 30 | 31 | const downloadedPackage = await NativeCodePush.downloadUpdate(updatePackageCopy, !!downloadProgressCallback); 32 | 33 | return { ...downloadedPackage, ...local }; 34 | } finally { 35 | downloadProgressSubscription && downloadProgressSubscription.remove(); 36 | } 37 | }, 38 | 39 | isPending: false // A remote package could never be in a pending state 40 | }; 41 | }; 42 | 43 | const local = { 44 | async install(installMode = NativeCodePush.codePushInstallModeOnNextRestart, minimumBackgroundDuration = 0, updateInstalledCallback) { 45 | const localPackage = this; 46 | const localPackageCopy = Object.assign({}, localPackage); // In dev mode, React Native deep freezes any object queued over the bridge 47 | await NativeCodePush.installUpdate(localPackageCopy, installMode, minimumBackgroundDuration); 48 | updateInstalledCallback && updateInstalledCallback(); 49 | if (installMode == NativeCodePush.codePushInstallModeImmediate) { 50 | NativeCodePush.restartApp(false); 51 | } else { 52 | NativeCodePush.clearPendingRestart(); 53 | localPackage.isPending = true; // Mark the package as pending since it hasn't been applied yet 54 | } 55 | }, 56 | 57 | isPending: false // A local package wouldn't be pending until it was installed 58 | }; 59 | 60 | return { local, remote }; 61 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bravemobile/react-native-code-push", 3 | "version": "9.1.2", 4 | "description": "React Native plugin for the CodePush service", 5 | "main": "CodePush.js", 6 | "typings": "typings/react-native-code-push.d.ts", 7 | "homepage": "https://microsoft.github.io/code-push", 8 | "keywords": [ 9 | "react-native", 10 | "code", 11 | "push" 12 | ], 13 | "author": "Soomgo Mobile Team (originally Microsoft Corporation)", 14 | "license": "MIT", 15 | "bin": { 16 | "code-push": "cli/index.js" 17 | }, 18 | "scripts": { 19 | "clean": "shx rm -rf bin", 20 | "setup": "npm install --quiet --no-progress", 21 | "prebuild:tests": "npm run clean && npm run tslint", 22 | "build:tests": "tsc", 23 | "test": "npm run build:tests && npm run test:setup && npm run test:fast", 24 | "test:android": "npm run build:tests && npm run test:setup:android && npm run test:fast:android", 25 | "test:ios": "npm run build:tests && npm run test:setup:ios && npm run test:fast:ios", 26 | "test:setup": "mocha --recursive bin/test --android --ios --setup", 27 | "test:setup:android": "mocha --recursive bin/test --android --setup", 28 | "test:setup:ios": "mocha --recursive bin/test --ios --setup", 29 | "test:fast": "mocha --recursive bin/test --android --ios", 30 | "test:fast:android": "mocha --recursive bin/test --android", 31 | "test:fast:ios": "mocha --recursive bin/test --ios", 32 | "test:debugger:android": "mocha --recursive --inspect-brk=0.0.0.0 bin/test --android", 33 | "test:debugger:ios": "mocha --recursive --inspect-brk=0.0.0.0 bin/test --ios", 34 | "tslint": "tslint -c tslint.json test/**/*.ts", 35 | "publish": "npm publish --access=public", 36 | "eslint": "eslint --quiet .", 37 | "jest": "jest versioning/*" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "git+https://github.com/Soomgo-Mobile/react-native-code-push.git" 42 | }, 43 | "dependencies": { 44 | "commander": "^12.1.0", 45 | "hoist-non-react-statics": "^3.3.2", 46 | "semver": "^7.3.5", 47 | "shelljs": "^0.8.5", 48 | "yazl": "^3.3.1" 49 | }, 50 | "peerDependencies": { 51 | "react-native": "*" 52 | }, 53 | "devDependencies": { 54 | "@babel/core": "^7.26.0", 55 | "@babel/preset-env": "^7.26.0", 56 | "@eslint/js": "^9.13.0", 57 | "@types/assert": "^1.5.2", 58 | "@types/mkdirp": "^1.0.1", 59 | "@types/mocha": "^9.0.0", 60 | "@types/node": "^14.0.27", 61 | "@types/q": "^1.5.4", 62 | "@types/semver": "^7.5.8", 63 | "@types/shelljs": "^0.8.15", 64 | "archiver": "latest", 65 | "babel-jest": "^29.7.0", 66 | "body-parser": "latest", 67 | "code-push-plugin-testing-framework": "file:./code-push-plugin-testing-framework", 68 | "del": "v6.0.0", 69 | "eslint": "^9.13.0", 70 | "eslint-plugin-react": "^7.37.2", 71 | "express": "latest", 72 | "globals": "^15.11.0", 73 | "jest": "^29.7.0", 74 | "mkdirp": "latest", 75 | "mocha": "^9.2.0", 76 | "q": "^1.5.1", 77 | "shx": "^0.3.4", 78 | "slash": "^3.0.0", 79 | "ts-node": "^10.9.2", 80 | "tslint": "^6.1.3", 81 | "typescript": "5.0.4", 82 | "typescript-eslint": "^8.11.0" 83 | }, 84 | "engines": { 85 | "node": ">=18" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dependency: { 3 | platforms: { 4 | android: { 5 | packageImportPath: "import com.microsoft.codepush.react.CodePush;", 6 | packageInstance: 7 | "new CodePush(getApplicationContext(), BuildConfig.DEBUG)", 8 | sourceDir: './android/app' 9 | } 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /scripts/getFilesInFolder.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | var path = require("path"); 3 | 4 | // Utility function that collects the stats of every file in a directory 5 | // as well as in its subdirectories. 6 | function getFilesInFolder(folderName, fileList) { 7 | var folderFiles = fs.readdirSync(folderName); 8 | folderFiles.forEach(function(file) { 9 | var fileStats = fs.statSync(path.join(folderName, file)); 10 | if (fileStats.isDirectory()) { 11 | getFilesInFolder(path.join(folderName, file), fileList); 12 | } else { 13 | fileStats.path = path.join(folderName, file); 14 | fileList.push(fileStats); 15 | } 16 | }); 17 | } 18 | 19 | module.exports = getFilesInFolder; -------------------------------------------------------------------------------- /scripts/recordFilesBeforeBundleCommand.js: -------------------------------------------------------------------------------- 1 | /* 2 | * This script creates a snapshot of the contents in the resource directory 3 | * by creating a map with the modified time of all the files in the directory 4 | * and saving it to a temp file. This snapshot is later referenced in 5 | * "generatePackageHash.js" to figure out which files have changed or were 6 | * newly generated by the "react-native bundle" command. 7 | */ 8 | 9 | var fs = require("fs"); 10 | var path = require("path"); 11 | 12 | var getFilesInFolder = require("./getFilesInFolder"); 13 | 14 | 15 | var resourcesDir = process.argv[2]; 16 | var tempFileName = process.argv[3]; 17 | 18 | var tempFileLocalPath = path.join(require("os").tmpdir(), tempFileName); 19 | var resourceFiles = []; 20 | 21 | try { 22 | getFilesInFolder(resourcesDir, resourceFiles); 23 | } catch(error) { 24 | var targetPathNotFoundExceptionMessage = "\nResources directory path does not exist.\n"; 25 | targetPathNotFoundExceptionMessage += "Unable to find '" + resourcesDir; 26 | targetPathNotFoundExceptionMessage += "' directory. Please check version of Android Plugin for Gradle."; 27 | error.message += targetPathNotFoundExceptionMessage; 28 | throw error; 29 | } 30 | 31 | var fileToModifiedTimeMap = {}; 32 | 33 | resourceFiles.forEach(function(resourceFile) { 34 | fileToModifiedTimeMap[resourceFile.path.substring(resourcesDir.length)] = resourceFile.mtime.getTime(); 35 | }); 36 | 37 | fs.writeFile(tempFileLocalPath, JSON.stringify(fileToModifiedTimeMap), function(err) { 38 | if (err) { 39 | throw err; 40 | } 41 | }); -------------------------------------------------------------------------------- /test/template/android/app/src/main/java/com/testcodepush/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.testcodepush; 2 | 3 | import android.app.Application; 4 | import com.microsoft.codepush.react.CodePush; 5 | import android.content.Context; 6 | import com.facebook.react.PackageList; 7 | import com.facebook.react.ReactApplication; 8 | import com.facebook.react.ReactNativeHost; 9 | import com.facebook.react.ReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | import java.lang.reflect.InvocationTargetException; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = 17 | new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected String getJSBundleFile() { 25 | return CodePush.getJSBundleFile(); 26 | } 27 | 28 | @Override 29 | protected List getPackages() { 30 | @SuppressWarnings("UnnecessaryLocalVariable") 31 | List packages = new PackageList(this).getPackages(); 32 | // Packages that cannot be autolinked yet can be added manually here, for example: 33 | // packages.add(new MyReactNativePackage()); 34 | return packages; 35 | } 36 | 37 | @Override 38 | protected String getJSMainModuleName() { 39 | return "index"; 40 | } 41 | }; 42 | 43 | @Override 44 | public ReactNativeHost getReactNativeHost() { 45 | return mReactNativeHost; 46 | } 47 | 48 | @Override 49 | public void onCreate() { 50 | super.onCreate(); 51 | SoLoader.init(this, /* native exopackage */ false); 52 | initializeFlipper(this); // Remove this line if you don't want Flipper enabled 53 | } 54 | 55 | /** 56 | * Loads Flipper in React Native templates. 57 | * 58 | * @param context 59 | */ 60 | private static void initializeFlipper(Context context) { 61 | if (BuildConfig.DEBUG) { 62 | try { 63 | /* 64 | We use reflection here to pick up the class that initializes Flipper, 65 | since Flipper library is not available in release mode 66 | */ 67 | Class aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper"); 68 | aClass.getMethod("initializeFlipper", Context.class).invoke(null, context); 69 | } catch (ClassNotFoundException e) { 70 | e.printStackTrace(); 71 | } catch (NoSuchMethodException e) { 72 | e.printStackTrace(); 73 | } catch (IllegalAccessException e) { 74 | e.printStackTrace(); 75 | } catch (InvocationTargetException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/template/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | TestCodePush 3 | CODE_PUSH_ANDROID_DEPLOYMENT_KEY 4 | CODE_PUSH_SERVER_URL 5 | 6 | -------------------------------------------------------------------------------- /test/template/ios/TestCodePush/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | 5 | #import 6 | #import 7 | #import 8 | 9 | @implementation AppDelegate 10 | 11 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 12 | { 13 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 14 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 15 | moduleName:@"CODE_PUSH_TEST_APP_NAME" 16 | initialProperties:nil]; 17 | 18 | self.moduleName = @"TestCodePush"; 19 | // You can add your custom initial props in the dictionary below. 20 | // They will be passed down to the ViewController used by React Native. 21 | self.initialProps = @{}; 22 | 23 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 24 | } 25 | 26 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 27 | { 28 | return [CodePush bundleURL]; 29 | } 30 | 31 | /// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. 32 | /// 33 | /// @see: https://reactjs.org/blog/2022/03/29/react-v18.html 34 | /// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). 35 | /// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. 36 | - (BOOL)concurrentRootEnabled 37 | { 38 | return true; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /test/template/scenarios/scenarioCheckForUpdate.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | 3 | module.exports = { 4 | startTest: function (testApp) { 5 | CodePushWrapper.checkForUpdate(testApp); 6 | }, 7 | 8 | getScenarioName: function () { 9 | return "Check for Update"; 10 | } 11 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioCheckForUpdateCustomKey.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | 3 | module.exports = { 4 | startTest: function (testApp) { 5 | CodePushWrapper.checkForUpdate(testApp, undefined, undefined, "CUSTOM-DEPLOYMENT-KEY"); 6 | }, 7 | 8 | getScenarioName: function () { 9 | return "Check for Update Custom Key"; 10 | } 11 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioDisallowRestartImmediate.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePush.disallowRestart(); 7 | CodePushWrapper.checkAndInstall(testApp, 8 | () => { 9 | CodePush.allowRestart(); 10 | }, 11 | undefined, 12 | CodePush.InstallMode.IMMEDIATE, 13 | undefined, 14 | true 15 | ); 16 | }, 17 | 18 | getScenarioName: function () { 19 | return "disallowRestart"; 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /test/template/scenarios/scenarioDisallowRestartOnResume.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePush.disallowRestart(); 7 | CodePushWrapper.checkAndInstall(testApp, 8 | undefined, 9 | undefined, 10 | CodePush.InstallMode.ON_NEXT_RESUME, 11 | undefined, 12 | true 13 | ); 14 | }, 15 | 16 | getScenarioName: function () { 17 | return "disallowRestart"; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /test/template/scenarios/scenarioDisallowRestartOnSuspend.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePush.disallowRestart(); 7 | CodePushWrapper.checkAndInstall(testApp, 8 | undefined, 9 | undefined, 10 | CodePush.InstallMode.ON_NEXT_SUSPEND, 11 | undefined, 12 | true 13 | ); 14 | }, 15 | 16 | getScenarioName: function () { 17 | return "disallowRestart"; 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /test/template/scenarios/scenarioDownloadUpdate.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | 3 | module.exports = { 4 | startTest: function (testApp) { 5 | CodePushWrapper.checkForUpdate(testApp, 6 | CodePushWrapper.download.bind(undefined, testApp, undefined, undefined)); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Download Update"; 11 | } 12 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioInstall.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.checkAndInstall(testApp, undefined, undefined, CodePush.InstallMode.IMMEDIATE); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Install"; 11 | } 12 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioInstallOnRestartWithRevert.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.checkAndInstall(testApp, undefined, undefined, CodePush.InstallMode.ON_NEXT_RESTART); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Install on Restart with Revert"; 11 | } 12 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioInstallOnResumeWithRevert.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.checkAndInstall(testApp, undefined, undefined, CodePush.InstallMode.ON_NEXT_RESUME); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Install on Resume with Revert"; 11 | } 12 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioInstallOnSuspendWithRevert.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.checkAndInstall(testApp, undefined, undefined, CodePush.InstallMode.ON_NEXT_SUSPEND); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Install on Suspend with Revert"; 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /test/template/scenarios/scenarioInstallRestart2x.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.checkAndInstall(testApp, 7 | () => { 8 | CodePush.restartApp(); 9 | CodePush.restartApp(); 10 | } 11 | ); 12 | }, 13 | 14 | getScenarioName: function () { 15 | return "Install and Restart 2x"; 16 | } 17 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioInstallWithRevert.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.checkAndInstall(testApp, undefined, undefined, CodePush.InstallMode.IMMEDIATE); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Install with Revert"; 11 | } 12 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioRestart.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | testApp.sendCurrentAndPendingPackage() 7 | .then(() => { 8 | CodePushWrapper.sync(testApp, (status) => { 9 | if (status === CodePush.SyncStatus.UPDATE_INSTALLED) { 10 | testApp.sendCurrentAndPendingPackage().then(CodePush.restartApp); 11 | } 12 | }, undefined, { installMode: CodePush.InstallMode.ON_NEXT_RESTART }); 13 | }); 14 | }, 15 | 16 | getScenarioName: function () { 17 | return "Restart"; 18 | } 19 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioRestart2x.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePush.restartApp(true); 7 | CodePushWrapper.checkAndInstall(testApp, 8 | () => { 9 | CodePush.restartApp(true); 10 | } 11 | ); 12 | }, 13 | 14 | getScenarioName: function () { 15 | return "Restart2x"; 16 | } 17 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSync.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, { installMode: CodePush.InstallMode.IMMEDIATE }); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Sync"; 11 | } 12 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSync2x.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, { installMode: CodePush.InstallMode.IMMEDIATE }); 7 | CodePushWrapper.sync(testApp, undefined, undefined, { installMode: CodePush.InstallMode.IMMEDIATE }); 8 | }, 9 | 10 | getScenarioName: function () { 11 | return "Sync 2x"; 12 | } 13 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncMandatoryDefault.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, { installMode: CodePush.InstallMode.ON_NEXT_RESTART }); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Sync Mandatory Default"; 11 | } 12 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncMandatoryRestart.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, 7 | { 8 | installMode: CodePush.InstallMode.IMMEDIATE, 9 | mandatoryInstallMode: CodePush.InstallMode.ON_NEXT_RESTART 10 | }); 11 | }, 12 | 13 | getScenarioName: function () { 14 | return "Sync Mandatory Restart"; 15 | } 16 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncMandatoryResume.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, 7 | { 8 | installMode: CodePush.InstallMode.ON_NEXT_RESTART, 9 | mandatoryInstallMode: CodePush.InstallMode.ON_NEXT_RESUME 10 | }); 11 | }, 12 | 13 | getScenarioName: function () { 14 | return "Sync Mandatory Resume"; 15 | } 16 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncMandatorySuspend.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, 7 | { 8 | installMode: CodePush.InstallMode.IMMEDIATE, 9 | mandatoryInstallMode: CodePush.InstallMode.ON_NEXT_SUSPEND 10 | }); 11 | }, 12 | 13 | getScenarioName: function () { 14 | return "Sync Mandatory Suspend"; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncRestartDelay.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, 7 | { 8 | installMode: CodePush.InstallMode.ON_NEXT_RESTART, 9 | minimumBackgroundDuration: 15 10 | }); 11 | }, 12 | 13 | getScenarioName: function () { 14 | return "Sync Restart Delay"; 15 | } 16 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncResume.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, 7 | { installMode: CodePush.InstallMode.ON_NEXT_RESUME }); 8 | }, 9 | 10 | getScenarioName: function () { 11 | return "Sync Resume"; 12 | } 13 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncResumeDelay.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, 7 | { 8 | installMode: CodePush.InstallMode.ON_NEXT_RESUME, 9 | minimumBackgroundDuration: 5 10 | }); 11 | }, 12 | 13 | getScenarioName: function () { 14 | return "Sync Resume Delay"; 15 | } 16 | }; -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncSuspend.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, 7 | { installMode: CodePush.InstallMode.ON_NEXT_SUSPEND }); 8 | }, 9 | 10 | getScenarioName: function () { 11 | return "Sync Suspend"; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /test/template/scenarios/scenarioSyncSuspendDelay.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | CodePushWrapper.sync(testApp, undefined, undefined, 7 | { 8 | installMode: CodePush.InstallMode.ON_NEXT_SUSPEND, 9 | minimumBackgroundDuration: 5 10 | }); 11 | }, 12 | 13 | getScenarioName: function () { 14 | return "Sync Suspend Delay"; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /test/template/scenarios/updateDeviceReady.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | 3 | module.exports = { 4 | startTest: function (testApp) { 5 | testApp.readyAfterUpdate(); 6 | }, 7 | 8 | getScenarioName: function () { 9 | return "Bad Update"; 10 | } 11 | }; -------------------------------------------------------------------------------- /test/template/scenarios/updateNARConditional.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | testApp.readyAfterUpdate((responseBody) => { 7 | if (responseBody !== "SKIP_NOTIFY_APPLICATION_READY") { 8 | CodePush.notifyAppReady(); 9 | CodePushWrapper.checkAndInstall(testApp, undefined, undefined, CodePush.InstallMode.ON_NEXT_RESTART); 10 | } else { 11 | testApp.setStateAndSendMessage("Skipping notifyApplicationReady!", "SKIPPED_NOTIFY_APPLICATION_READY"); 12 | } 13 | }); 14 | }, 15 | 16 | getScenarioName: function () { 17 | return "Conditional Update"; 18 | } 19 | }; -------------------------------------------------------------------------------- /test/template/scenarios/updateNotifyApplicationReady.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | import CodePush from "react-native-code-push"; 3 | 4 | module.exports = { 5 | startTest: function (testApp) { 6 | testApp.readyAfterUpdate(); 7 | CodePush.notifyAppReady(); 8 | }, 9 | 10 | getScenarioName: function () { 11 | return "Good Update"; 12 | } 13 | }; -------------------------------------------------------------------------------- /test/template/scenarios/updateSync.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | 3 | module.exports = { 4 | startTest: function (testApp) { 5 | testApp.readyAfterUpdate(); 6 | CodePushWrapper.sync(testApp); 7 | }, 8 | 9 | getScenarioName: function () { 10 | return "Good Update (w/ Sync)"; 11 | } 12 | }; -------------------------------------------------------------------------------- /test/template/scenarios/updateSync2x.js: -------------------------------------------------------------------------------- 1 | var CodePushWrapper = require("../codePushWrapper.js"); 2 | 3 | module.exports = { 4 | startTest: function (testApp) { 5 | testApp.readyAfterUpdate(); 6 | CodePushWrapper.sync(testApp); 7 | CodePushWrapper.sync(testApp); 8 | }, 9 | 10 | getScenarioName: function () { 11 | return "Good Update (w/ Sync 2x)"; 12 | } 13 | }; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "commonjs", 5 | "lib": ["es6"], 6 | "noImplicitAny": true, 7 | "noEmitOnError": true, 8 | "moduleResolution": "node", 9 | "sourceMap": true, 10 | "rootDir": "test", 11 | "outDir": "bin", 12 | "removeComments": true 13 | }, 14 | "exclude": [ 15 | "Examples/CodePushDemoApp", 16 | "code-push.config.example.*" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "rules": { 4 | "class-name": true, 5 | "comment-format": [true, 6 | "check-space" 7 | ], 8 | "indent": [true, 9 | "spaces" 10 | ], 11 | "one-line": [true, 12 | "check-open-brace" 13 | ], 14 | "quotemark": [true, 15 | "double" 16 | ], 17 | "semicolon": true, 18 | "whitespace": [true, 19 | "check-branch", 20 | "check-operator", 21 | "check-separator", 22 | "check-type" 23 | ], 24 | "typedef-whitespace": [true, { 25 | "call-signature": "nospace", 26 | "index-signature": "nospace", 27 | "parameter": "nospace", 28 | "property-declaration": "nospace", 29 | "variable-declaration": "nospace" 30 | }] 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /versioning/BaseVersioning.test.js: -------------------------------------------------------------------------------- 1 | import { BaseVersioning } from "./BaseVersioning" 2 | 3 | describe('BaseVersioning', () => { 4 | describe('constructor', () => { 5 | it('should throw an error if it is directly instantiated', () => { 6 | expect(() => new BaseVersioning({})).toThrow("Abstract classes can't be instantiated.") 7 | }) 8 | 9 | it('should throw an error if releaseHistory is not defined', () => { 10 | class TestVersioning extends BaseVersioning {} 11 | expect(() => new TestVersioning()).toThrow("param releaseHistory and sortingMethod is needed") 12 | expect(() => new TestVersioning({}, () => {})).not.toThrow("param releaseHistory and sortingMethod is needed") 13 | }) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /versioning/IncrementalVersioning.js: -------------------------------------------------------------------------------- 1 | const { BaseVersioning } = require("./BaseVersioning"); 2 | 3 | class IncrementalVersioning extends BaseVersioning { 4 | constructor(releaseHistory) { 5 | super(releaseHistory, (v1, v2) => Number(v2) - Number(v1)); 6 | } 7 | } 8 | 9 | module.exports = { IncrementalVersioning }; 10 | -------------------------------------------------------------------------------- /versioning/SemverVersioning.js: -------------------------------------------------------------------------------- 1 | const Semver = require("semver"); 2 | const { BaseVersioning } = require("./BaseVersioning"); 3 | 4 | class SemverVersioning extends BaseVersioning { 5 | constructor(releaseHistory) { 6 | super(releaseHistory, (v1, v2) => (Semver.gt(v1, v2) ? -1 : 1)); 7 | } 8 | } 9 | 10 | module.exports = { SemverVersioning }; 11 | -------------------------------------------------------------------------------- /versioning/index.js: -------------------------------------------------------------------------------- 1 | const { BaseVersioning } = require("./BaseVersioning"); 2 | const { SemverVersioning } = require("./SemverVersioning"); 3 | 4 | module.exports = { 5 | SemverVersioning, 6 | BaseVersioning, 7 | }; 8 | --------------------------------------------------------------------------------