├── .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 | [](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 |
--------------------------------------------------------------------------------