1
0
mirror of https://gitcode.com/gh_mirrors/re/react-native-pushy.git synced 2025-09-16 12:51:44 +08:00
Code Issues Packages Projects Releases Wiki Activity GitHub Gitee

Compare commits

...

60 Commits

Author SHA1 Message Date
sunnylqm
680f77a8d8 v5.5.10 2020-04-30 18:09:05 +08:00
sunnylqm
eaae1286f4 Add link error hint 2020-04-30 17:59:28 +08:00
sunnylqm
20a21ae894 v5.5.9 2020-04-14 22:59:19 +08:00
sunnylqm
5abe7b181b Fix ios script path 2020-04-14 22:58:53 +08:00
Sunny Luo
e859238d97 Update changelog.md 2020-04-07 16:38:06 +08:00
sunnylqm
7796090e72 v5.5.8 2020-04-02 12:09:38 +08:00
sunnylqm
f07be8cf0b Merge branch 'customInstanceManager' 2020-04-02 12:09:13 +08:00
sunnylqm
7c8f2d493f import instancemanager 2020-04-02 12:06:32 +08:00
sunnylqm
437eb4f10f v5.5.7 2020-04-02 00:48:46 +08:00
sunnylqm
5053a89cfa backup domain 2020-04-02 00:48:19 +08:00
Sunny Luo
4a84622c04 Update domains.json 2020-04-02 00:44:18 +08:00
Sunny Luo
cf812eacda Update domains.json 2020-04-02 00:43:21 +08:00
sunnylqm
6a031f598f Allow custom instance manager 2020-03-13 13:13:37 +08:00
sunnylqm
7ecf879ac9 Merge branch 'master' of github.com:reactnativecn/react-native-pushy 2020-03-13 11:28:09 +08:00
Sunny Luo
c779befa86 Update README.md 2020-03-13 09:36:47 +08:00
Sunny Luo
19f060abf5 Update react-native-update.podspec 2020-03-03 14:55:32 +08:00
sunnylqm
b9c655312e Fix typo in patch extension 2020-02-27 00:00:49 +08:00
sunnylqm
2ad4635bc6 Allow http for android example 2020-02-27 00:00:05 +08:00
sunnylqm
0d3f95d2bd Move cli to separated repo 2020-02-19 00:34:36 +08:00
sunnylqm
36568356dc v5.5.6 2020-02-11 11:56:59 +08:00
sunnylqm
1a3681f308 Update changelog 2020-02-11 11:56:38 +08:00
sunnylqm
81f6e57413 Check ppk extension when publish 2020-02-11 11:53:30 +08:00
sunnylqm
46c7aa88dd Clarify packageVersion and currentVersion 2020-02-11 11:32:18 +08:00
Sunny Luo
22a2902f71 Fix download buffer overflow 2020-02-07 22:28:17 +08:00
sunnylqm
e9755392ec Force reset cache when bundle 2020-01-20 15:05:12 +08:00
sunnylqm
c28eec436d immediate reject 2020-01-19 00:13:50 +08:00
sunnylqm
c46fb1be3a v5.5.5 2020-01-18 23:57:20 +08:00
sunnylqm
1d0fb7d260 Backup domains 2020-01-18 23:55:10 +08:00
sunnylqm
bdd97ac6ac domains.json 2020-01-18 21:46:55 +08:00
sunnylqm
ba3e92e3b8 5.5.4 changelog 2020-01-13 22:25:59 +08:00
sunnylqm
3939607f49 v5.5.4 2020-01-13 22:23:10 +08:00
sunnylqm
8d06235ea3 Check android bundle file before reload 2020-01-13 21:58:33 +08:00
sunnylqm
fa754ef8a2 Check bundlejs before switch 2020-01-09 17:45:44 +08:00
sunnylqm
5a5884c8fa Fix doc link 2019-12-29 12:42:05 +08:00
sunnylqm
8bd92f2c0e Fix doc home page 2019-12-29 12:39:45 +08:00
sunnylqm
3ac2e3ef05 文档优化 2019-12-29 12:36:08 +08:00
Sunny Luo
523cbdd7e1 Update README.md 2019-12-23 10:28:02 +08:00
sunnylqm
dece044a54 Bump 5.5.3 2019-12-18 18:06:08 +08:00
sunnylqm
a35c98ed4d v5.5.3 2019-12-18 18:05:47 +08:00
sunnylqm
dcea576fff Ignore hermes output to avoid buffer issue 2019-12-18 18:04:08 +08:00
Sunny Luo
201f11e770 Update guide.md 2019-12-14 12:26:04 +08:00
Sunny Luo
3a86218a48 Update guide.md 2019-12-14 12:24:39 +08:00
sunnylqm
5a5e27037c Update yauzl 2019-12-13 11:01:04 +08:00
sunnylqm
9b4016ba0a Update changelog 2019-12-06 15:27:26 +08:00
sunnylqm
5202cbec96 v5.5.2 2019-12-06 15:25:46 +08:00
sunnylqm
d7c7e27eaa 修复use_frameworks时无法获取编译时间戳的问题 2019-12-06 15:24:49 +08:00
Sunny Luo
e9e60bc5c6 Merge pull request #299 from nfq6612/progress
android 下载进度发送事件发出和发送事件频率优化
2019-12-04 11:12:22 +08:00
nonghuaqiang
a93875884c android 下载进度发送事件发出和发送事件频率优化 2019-12-03 12:34:02 +08:00
sunnylqm
f20263b827 v5.5.1 2019-11-25 16:09:34 +08:00
sunnylqm
8a74678b9c Add missing files 2019-11-25 16:09:05 +08:00
Sunny Luo
17b7920100 Merge pull request #296 from reactnativecn/dependabot/npm_and_yarn/brace-expansion-1.1.11
Bump brace-expansion from 1.1.6 to 1.1.11
2019-11-24 22:30:18 +08:00
sunnylqm
4d44eb858f Remove unused fs-promise 2019-11-24 22:30:04 +08:00
sunnylqm
4d502a4e73 v5.5.0 2019-11-24 22:01:48 +08:00
sunnylqm
7488a7fb9f Update changelog 2019-11-24 22:01:00 +08:00
dependabot[bot]
74ef45056e Bump brace-expansion from 1.1.6 to 1.1.11
Bumps [brace-expansion](https://github.com/juliangruber/brace-expansion) from 1.1.6 to 1.1.11.
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/v1.1.6...1.1.11)

Signed-off-by: dependabot[bot] <support@github.com>
2019-11-24 13:58:51 +00:00
sunnylqm
6b254582b6 Read build time for ipa 2019-11-24 21:58:12 +08:00
sunnylqm
737060e962 Read build time in local-cli for apk 2019-11-21 00:02:00 +08:00
sunnylqm
b848259905 v5.4.0 2019-11-16 00:34:14 +08:00
sunnylqm
6c087b473b Update readme 2019-11-16 00:33:34 +08:00
sunnylqm
ddc12186b0 Rename 2019-11-16 00:31:30 +08:00
101 changed files with 1594 additions and 2920 deletions

3
.gitignore vendored
View File

@@ -1,8 +1,5 @@
/.idea
/node_modules
/local-cli/lib
/react-native-pushy-cli/node_modules
/react-native-pushy-cli/lib
/android/build
/android/obj
*.iml

View File

@@ -4,8 +4,6 @@
/.eslintrc
/.nvmrc
/.travis.yml
/local-cli/src
/react-native-pushy-cli
/Example
/android/build
@@ -45,4 +43,6 @@ node_modules/
npm-debug.log
Example
yarn.lock
android/jni
android/jni
domains.json

View File

@@ -1,5 +1,4 @@
module.exports = {
printWidth: 120,
trailingComma: 'all',
singleQuote: true,
};

View File

@@ -4,6 +4,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<application
android:usesCleartextTraffic="true"
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"

View File

@@ -1,14 +1,14 @@
PODS:
- boost-for-react-native (1.63.0)
- DoubleConversion (1.1.6)
- FBLazyVector (0.61.2)
- FBReactNativeSpec (0.61.2):
- FBLazyVector (0.61.4)
- FBReactNativeSpec (0.61.4):
- Folly (= 2018.10.22.00)
- RCTRequired (= 0.61.2)
- RCTTypeSafety (= 0.61.2)
- React-Core (= 0.61.2)
- React-jsi (= 0.61.2)
- ReactCommon/turbomodule/core (= 0.61.2)
- RCTRequired (= 0.61.4)
- RCTTypeSafety (= 0.61.4)
- React-Core (= 0.61.4)
- React-jsi (= 0.61.4)
- ReactCommon/turbomodule/core (= 0.61.4)
- Folly (2018.10.22.00):
- boost-for-react-native
- DoubleConversion
@@ -19,215 +19,215 @@ PODS:
- DoubleConversion
- glog
- glog (0.3.5)
- RCTRequired (0.61.2)
- RCTTypeSafety (0.61.2):
- FBLazyVector (= 0.61.2)
- RCTRequired (0.61.4)
- RCTTypeSafety (0.61.4):
- FBLazyVector (= 0.61.4)
- Folly (= 2018.10.22.00)
- RCTRequired (= 0.61.2)
- React-Core (= 0.61.2)
- React (0.61.2):
- React-Core (= 0.61.2)
- React-Core/DevSupport (= 0.61.2)
- React-Core/RCTWebSocket (= 0.61.2)
- React-RCTActionSheet (= 0.61.2)
- React-RCTAnimation (= 0.61.2)
- React-RCTBlob (= 0.61.2)
- React-RCTImage (= 0.61.2)
- React-RCTLinking (= 0.61.2)
- React-RCTNetwork (= 0.61.2)
- React-RCTSettings (= 0.61.2)
- React-RCTText (= 0.61.2)
- React-RCTVibration (= 0.61.2)
- React-Core (0.61.2):
- RCTRequired (= 0.61.4)
- React-Core (= 0.61.4)
- React (0.61.4):
- React-Core (= 0.61.4)
- React-Core/DevSupport (= 0.61.4)
- React-Core/RCTWebSocket (= 0.61.4)
- React-RCTActionSheet (= 0.61.4)
- React-RCTAnimation (= 0.61.4)
- React-RCTBlob (= 0.61.4)
- React-RCTImage (= 0.61.4)
- React-RCTLinking (= 0.61.4)
- React-RCTNetwork (= 0.61.4)
- React-RCTSettings (= 0.61.4)
- React-RCTText (= 0.61.4)
- React-RCTVibration (= 0.61.4)
- React-Core (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default (= 0.61.2)
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-Core/Default (= 0.61.4)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/CoreModulesHeaders (0.61.2):
- React-Core/CoreModulesHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/Default (0.61.2):
- React-Core/Default (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/DevSupport (0.61.2):
- React-Core/DevSupport (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default (= 0.61.2)
- React-Core/RCTWebSocket (= 0.61.2)
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-jsinspector (= 0.61.2)
- React-Core/Default (= 0.61.4)
- React-Core/RCTWebSocket (= 0.61.4)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- React-jsinspector (= 0.61.4)
- Yoga
- React-Core/RCTActionSheetHeaders (0.61.2):
- React-Core/RCTActionSheetHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTAnimationHeaders (0.61.2):
- React-Core/RCTAnimationHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTBlobHeaders (0.61.2):
- React-Core/RCTBlobHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTImageHeaders (0.61.2):
- React-Core/RCTImageHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTLinkingHeaders (0.61.2):
- React-Core/RCTLinkingHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTNetworkHeaders (0.61.2):
- React-Core/RCTNetworkHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTSettingsHeaders (0.61.2):
- React-Core/RCTSettingsHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTTextHeaders (0.61.2):
- React-Core/RCTTextHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTVibrationHeaders (0.61.2):
- React-Core/RCTVibrationHeaders (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-Core/RCTWebSocket (0.61.2):
- React-Core/RCTWebSocket (0.61.4):
- Folly (= 2018.10.22.00)
- glog
- React-Core/Default (= 0.61.2)
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsiexecutor (= 0.61.2)
- React-Core/Default (= 0.61.4)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsiexecutor (= 0.61.4)
- Yoga
- React-CoreModules (0.61.2):
- FBReactNativeSpec (= 0.61.2)
- React-CoreModules (0.61.4):
- FBReactNativeSpec (= 0.61.4)
- Folly (= 2018.10.22.00)
- RCTTypeSafety (= 0.61.2)
- React-Core/CoreModulesHeaders (= 0.61.2)
- React-RCTImage (= 0.61.2)
- ReactCommon/turbomodule/core (= 0.61.2)
- React-cxxreact (0.61.2):
- RCTTypeSafety (= 0.61.4)
- React-Core/CoreModulesHeaders (= 0.61.4)
- React-RCTImage (= 0.61.4)
- ReactCommon/turbomodule/core (= 0.61.4)
- React-cxxreact (0.61.4):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-jsinspector (= 0.61.2)
- React-jsi (0.61.2):
- React-jsinspector (= 0.61.4)
- React-jsi (0.61.4):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-jsi/Default (= 0.61.2)
- React-jsi/Default (0.61.2):
- React-jsi/Default (= 0.61.4)
- React-jsi/Default (0.61.4):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-jsiexecutor (0.61.2):
- React-jsiexecutor (0.61.4):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- React-jsinspector (0.61.2)
- react-native-update (5.3.0):
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- React-jsinspector (0.61.4)
- react-native-update (5.5.8):
- React
- react-native-update/BSDiff (= 5.3.0)
- react-native-update/RCTHotUpdate (= 5.3.0)
- react-native-update/BSDiff (= 5.5.8)
- react-native-update/RCTPushy (= 5.5.8)
- SSZipArchive
- react-native-update/BSDiff (5.3.0):
- react-native-update/BSDiff (5.5.8):
- React
- SSZipArchive
- react-native-update/RCTHotUpdate (5.3.0):
- react-native-update/RCTPushy (5.5.8):
- React
- SSZipArchive
- React-RCTActionSheet (0.61.2):
- React-Core/RCTActionSheetHeaders (= 0.61.2)
- React-RCTAnimation (0.61.2):
- React-Core/RCTAnimationHeaders (= 0.61.2)
- React-RCTBlob (0.61.2):
- React-Core/RCTBlobHeaders (= 0.61.2)
- React-Core/RCTWebSocket (= 0.61.2)
- React-jsi (= 0.61.2)
- React-RCTNetwork (= 0.61.2)
- React-RCTImage (0.61.2):
- React-Core/RCTImageHeaders (= 0.61.2)
- React-RCTNetwork (= 0.61.2)
- React-RCTLinking (0.61.2):
- React-Core/RCTLinkingHeaders (= 0.61.2)
- React-RCTNetwork (0.61.2):
- React-Core/RCTNetworkHeaders (= 0.61.2)
- React-RCTSettings (0.61.2):
- React-Core/RCTSettingsHeaders (= 0.61.2)
- React-RCTText (0.61.2):
- React-Core/RCTTextHeaders (= 0.61.2)
- React-RCTVibration (0.61.2):
- React-Core/RCTVibrationHeaders (= 0.61.2)
- ReactCommon/jscallinvoker (0.61.2):
- React-RCTActionSheet (0.61.4):
- React-Core/RCTActionSheetHeaders (= 0.61.4)
- React-RCTAnimation (0.61.4):
- React-Core/RCTAnimationHeaders (= 0.61.4)
- React-RCTBlob (0.61.4):
- React-Core/RCTBlobHeaders (= 0.61.4)
- React-Core/RCTWebSocket (= 0.61.4)
- React-jsi (= 0.61.4)
- React-RCTNetwork (= 0.61.4)
- React-RCTImage (0.61.4):
- React-Core/RCTImageHeaders (= 0.61.4)
- React-RCTNetwork (= 0.61.4)
- React-RCTLinking (0.61.4):
- React-Core/RCTLinkingHeaders (= 0.61.4)
- React-RCTNetwork (0.61.4):
- React-Core/RCTNetworkHeaders (= 0.61.4)
- React-RCTSettings (0.61.4):
- React-Core/RCTSettingsHeaders (= 0.61.4)
- React-RCTText (0.61.4):
- React-Core/RCTTextHeaders (= 0.61.4)
- React-RCTVibration (0.61.4):
- React-Core/RCTVibrationHeaders (= 0.61.4)
- ReactCommon/jscallinvoker (0.61.4):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-cxxreact (= 0.61.2)
- ReactCommon/turbomodule/core (0.61.2):
- React-cxxreact (= 0.61.4)
- ReactCommon/turbomodule/core (0.61.4):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React-Core (= 0.61.2)
- React-cxxreact (= 0.61.2)
- React-jsi (= 0.61.2)
- ReactCommon/jscallinvoker (= 0.61.2)
- React-Core (= 0.61.4)
- React-cxxreact (= 0.61.4)
- React-jsi (= 0.61.4)
- ReactCommon/jscallinvoker (= 0.61.4)
- SSZipArchive (2.2.2)
- Yoga (1.14.0)
@@ -324,33 +324,33 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
FBLazyVector: 68b6a76960fbd8ecd9fb7ce0aadd3329c3340a99
FBReactNativeSpec: 5a764c60abdc3336a213e5310c40b74741f32839
FBLazyVector: feb35a6b7f7b50f367be07f34012f34a79282fa3
FBReactNativeSpec: 51477b84b1bf7ab6f9ef307c24e3dd675391be44
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
RCTRequired: c639d59ed389cfb1f1203f65c2ea946d8ec586e2
RCTTypeSafety: dc23fb655d6c77667c78e327bf661bc11e3b8aec
React: 7e586e5d7bec12b91c1a096826b0fc9ab1da7865
React-Core: 8ddb9770b4a30a6ab4a754e6ed5ec76454e3d699
React-CoreModules: b3d9eece8ad7df36c917a41f05c1168c52fe0b34
React-cxxreact: 1f972757c0bd08d962ef78068e06613c27489a3f
React-jsi: 32285a21b1b24c36060493ed3057a34677d58d09
React-jsiexecutor: 8909917ff7d8f21a57e443a866fd8d4560e50c65
React-jsinspector: 111d7d342b07a904c400592e02a2b958f1098b60
react-native-update: 5efc26c6af746154a6b7f7c4c073b9ff3f418322
React-RCTActionSheet: 89b037c0fb7d2671607cb645760164e7e0c013f6
React-RCTAnimation: e3cefa93c38c004c318f7ec04b883eb14b8b8235
React-RCTBlob: d26ac0e313fbf14e7203473fd593ccaaeee8329e
React-RCTImage: 4bdd9588783fa9e48ef669ccd4f747224e208edf
React-RCTLinking: 65f0088ff463babd3d5d567964a65b74141eff3b
React-RCTNetwork: 0c1a73576c1cfeafe68396556de1b17d93c0c595
React-RCTSettings: 4194f1f0edbddf3fd44d1714dc6578bb20379b60
React-RCTText: e3ef6191cdb627855ff7fe8fa0c1e14094967fb8
React-RCTVibration: fb54c732fd20405a76598e431aa2f8c2bf527de9
ReactCommon: 5848032ed2f274fcb40f6b9ec24067787c42d479
RCTRequired: f3b3fb6f4723e8e52facb229d0c75fdc76773849
RCTTypeSafety: 2ec60de6abb1db050b56ecc4b60188026078fd10
React: 10e0130b57e55a7cd8c3dee37c1261102ce295f4
React-Core: 636212410772d05f3a1eb79d965df2962ca1c70b
React-CoreModules: 6f70d5e41919289c582f88c9ad9923fe5c87400a
React-cxxreact: ddecbe9157ec1743f52ea17bf8d95debc0d6e846
React-jsi: ca921f4041505f9d5197139b2d09eeb020bb12e8
React-jsiexecutor: 8dfb73b987afa9324e4009bdce62a18ce23d983c
React-jsinspector: d15478d0a8ada19864aa4d1cc1c697b41b3fa92f
react-native-update: 491cb5902a18f1ebeeb903f1a619df94662fc52a
React-RCTActionSheet: 7369b7c85f99b6299491333affd9f01f5a130c22
React-RCTAnimation: d07be15b2bd1d06d89417eb0343f98ffd2b099a7
React-RCTBlob: 8e0b23d95c9baa98f6b0e127e07666aaafd96c34
React-RCTImage: 443050d14a66e8c2332e9c055f45689d23e15cc7
React-RCTLinking: ce9a90ba155aec41be49e75ec721bbae2d48a47e
React-RCTNetwork: 41fe54bacc67dd00e6e4c4d30dd98a13e4beabc8
React-RCTSettings: 45e3e0a6470310b2dab2ccc6d1d73121ba3ea936
React-RCTText: 21934e0a51d522abcd0a275407e80af45d6fd9ec
React-RCTVibration: 0f76400ee3cec6edb9c125da49fed279340d145a
ReactCommon: a6a294e7028ed67b926d29551aa9394fd989c24c
SSZipArchive: fa16b8cc4cdeceb698e5e5d9f67e9558532fbf23
Yoga: 14927e37bd25376d216b150ab2a561773d57911f
Yoga: ba3d99dbee6c15ea6bbe3783d1f0cb1ffb79af0f
PODFILE CHECKSUM: 244ba888c650d3945bf72a8d01516fb0f1b3b097
COCOAPODS: 1.8.1
COCOAPODS: 1.9.1

View File

@@ -135,6 +135,7 @@
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
1CD78CE8A2E7B88FAE48FCEE /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -200,6 +201,24 @@
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
};
1CD78CE8A2E7B88FAE48FCEE /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-testHotUpdate/Pods-testHotUpdate-resources.sh",
"${PODS_ROOT}/../../../../ios/pushy_build_time.txt",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/pushy_build_time.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-testHotUpdate/Pods-testHotUpdate-resources.sh\"\n";
showEnvVarsInLog = 0;
};
C49C562FA1BDE1F80ECD9C13 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;

View File

@@ -10,7 +10,7 @@
#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#import "RCTHotUpdate.h"
#import "RCTPushy.h"
@implementation AppDelegate
@@ -37,7 +37,7 @@
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
// DEBUGbundle
return [RCTHotUpdate bundleURL];
return [RCTPushy bundleURL];
#endif
}

View File

@@ -11,7 +11,7 @@
},
"dependencies": {
"react": "16.9.0",
"react-native": "0.61.2",
"react-native": "0.61.4",
"react-native-update": "link:../.."
},
"devDependencies": {

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +1,10 @@
# react-native-update [![npm version](https://badge.fury.io/js/react-native-update.svg)](http://badge.fury.io/js/react-native-update)
本组件是面向 React Native 提供热更新功能的组件,建议结合[Update 服务](https://update.reactnative.cn/)使用。
本组件是面向 React Native 提供热更新功能的组件,结合[Update 服务](https://update.reactnative.cn/)使用。
注意,在 iOS 上使用热更新有被拒的可能。可以按以下步骤屏蔽 iOS 端(`react-native-update`版本需 >= 5.3.2)
### 快速开始
1. 如果 RN 版本>=0.60,在项目根目录下编辑或创建 react-native.config.js添加如下内容
```js
// react-native.config.js
module.exports = {
dependencies: {
'react-native-update': {
platforms: {
ios: null, // 阻止ios模块自动链接
},
},
},
};
```
2. 如果在原生代码端尚未配置,则跳过下面文档中的 ios 端的配置。如果已经配置,则按文档的步骤反向操作(添加的 ios 代码删去)。
3. 如果是 0.60 以上版本或使用了 cocoapods在 ios 目录中再次运行 pod install确保 Podfile 和 Podfile.lock 中都没有'react-native-update'。如果 RN 版本<0.60则运行`react-native unlink react-native-update`
4. js 代码里调用 checkUpdate()方法前判断 Platform.OS如果是 ios 平台则直接 return 跳过
### 最近更新
请查阅[最近更新文档](CHANGELOG.md)
请查看[文档](https://reactnativecn.github.io/react-native-pushy)
### 优势
@@ -35,26 +14,6 @@ module.exports = {
4. meta 信息及开放 API提供更高扩展性。
5. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
### 安装与快速入门
请查阅
- [文档-快速入门-准备工作](docs/guide.md)
- [文档-快速入门-添加热更新功能](docs/guide2.md)
- [文档-快速入门-发布版本](docs/guide3.md)
- [文档-常见问题与高级指南](docs/faq_advance.md)
### 命令行工具
请查阅[文档-命令行工具](docs/cli.md)
### API 接口
请查阅[文档-API 接口](docs/api.md)
### 本地开发
```
@@ -70,4 +29,4 @@ $ yarn start
本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。
关于此插件发现任何问题可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)或者[中文社区](http://bbs.reactnative.cn/category/7)发帖提问
关于此插件发现任何问题可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)发帖提问。

View File

@@ -27,11 +27,9 @@ android {
buildTypes {
release {
buildConfigField("String", "PUSHY_BUILD_TIME", "\"${minutesSinceEpoch}\"")
resValue("string", "pushy_build_time", "${minutesSinceEpoch}")
}
debug {
buildConfigField("String", "PUSHY_BUILD_TIME", "\"0\"")
resValue("string", "pushy_build_time", "0")
}
}

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="cn.reactnative.modules.update">
<application>
<meta-data android:name="pushy_build_time" android:value="@string/pushy_build_time" />
</application>
</manifest>

View File

@@ -3,6 +3,8 @@ package cn.reactnative.modules.update;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import okhttp3.OkHttpClient;
import okhttp3.Request;
@@ -31,11 +33,11 @@ import java.util.HashMap;
import okio.BufferedSink;
import okio.BufferedSource;
import okio.Okio;
import static cn.reactnative.modules.update.UpdateModule.sendEvent;
/**
* Created by tdzl2003 on 3/31/16.
*/
class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
final int DOWNLOAD_CHUNK_SIZE = 4096;
Context context;
@@ -91,11 +93,18 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
long bytesRead = 0;
long totalRead = 0;
double lastProgressValue=0;
while ((bytesRead = source.read(sink.buffer(), DOWNLOAD_CHUNK_SIZE)) != -1) {
totalRead += bytesRead;
sink.emit();
if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Progress " + totalRead + "/" + contentLength);
}
double progress = Math.round(((double) totalRead * 100) / contentLength);
if ((progress != lastProgressValue) || (totalRead == contentLength)) {
lastProgressValue = progress;
publishProgress(new long[]{(long)progress,totalRead, contentLength});
}
}
if (totalRead != contentLength) {
throw new Error("Unexpected eof while reading ppk");
@@ -108,6 +117,17 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
}
}
@Override
protected void onProgressUpdate(long[]... values) {
super.onProgressUpdate(values);
WritableMap params = Arguments.createMap();
params.putDouble("progress", (values[0][0]));
params.putDouble("totalRead", (values[0][1]));
params.putDouble("contentLength", (values[0][2]));
sendEvent("progress", params);
}
byte[] buffer = new byte[1024];
private static native byte[] bsdiffPatch(byte[] origin, byte[] patch);

View File

@@ -4,6 +4,9 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;
import com.facebook.react.ReactInstanceManager;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -19,6 +22,7 @@ public class UpdateContext {
private Executor executor;
public static boolean DEBUG = false;
private static ReactInstanceManager mReactInstanceManager;
public UpdateContext(Context context) {
this.context = context;
@@ -60,7 +64,7 @@ public class UpdateContext {
}
public String getBuildTime() {
return BuildConfig.PUSHY_BUILD_TIME;
return context.getString(R.string.pushy_build_time);
}
public interface DownloadFileListener {
@@ -106,8 +110,8 @@ public class UpdateContext {
private SharedPreferences sp;
public void switchVersion(String hashName) {
if (!new File(rootDir, hashName).exists()) {
throw new Error("Hash name not found, must download first.");
if (!new File(rootDir, hashName+"/index.bundlejs").exists()) {
throw new Error("Bundle version " + hashName + " not found.");
}
String lastVersion = getCurrentVersion();
SharedPreferences.Editor editor = sp.edit();
@@ -158,6 +162,15 @@ public class UpdateContext {
this.clearUp();
}
public static void setCustomInstanceManager(ReactInstanceManager instanceManager) {
mReactInstanceManager = instanceManager;
}
public ReactInstanceManager getCustomReactInstanceManager() {
return mReactInstanceManager;
}
public static String getBundleUrl(Context context) {
return new UpdateContext(context.getApplicationContext()).getBundleUrl();
}
@@ -182,10 +195,18 @@ public class UpdateContext {
currentVersion = this.rollBack();
}
}
if (currentVersion == null) {
return defaultAssetsUrl;
while (currentVersion != null) {
File bundleFile = new File(rootDir, currentVersion+"/index.bundlejs");
if (!bundleFile.exists()) {
Log.e("getBundleUrl", "Bundle version " + currentVersion + " not found.");
currentVersion = this.rollBack();
continue;
}
return bundleFile.toString();
}
return (new File(rootDir, currentVersion+"/index.bundlejs").toString());
return defaultAssetsUrl;
}
private String rollBack() {

View File

@@ -9,11 +9,14 @@ import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.bridge.JSBundleLoader;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@@ -25,10 +28,11 @@ import java.util.Map;
*/
public class UpdateModule extends ReactContextBaseJavaModule{
UpdateContext updateContext;
public static ReactApplicationContext mContext;
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
super(reactContext);
this.updateContext = updateContext;
mContext=reactContext;
}
public UpdateModule(ReactApplicationContext reactContext) {
@@ -57,7 +61,7 @@ public class UpdateModule extends ReactContextBaseJavaModule{
@Override
public String getName() {
return "RCTHotUpdate";
return "RCTPushy";
}
@ReactMethod
@@ -119,11 +123,15 @@ public class UpdateModule extends ReactContextBaseJavaModule{
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.switchVersion(hash);
try {
updateContext.switchVersion(hash);
Activity activity = getCurrentActivity();
Application application = activity.getApplication();
ReactInstanceManager instanceManager = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
ReactInstanceManager instanceManager = updateContext.getCustomReactInstanceManager();
if (instanceManager == null) {
instanceManager = ((ReactApplication) application).getReactNativeHost().getReactInstanceManager();
}
try {
Field jsBundleField = instanceManager.getClass().getDeclaredField("mJSBundleFile");
@@ -143,7 +151,7 @@ public class UpdateModule extends ReactContextBaseJavaModule{
}
} catch (Throwable err) {
Log.e("pushy", "Failed to restart application", err);
Log.e("pushy", "switchVersion failed", err);
}
}
});
@@ -156,7 +164,11 @@ public class UpdateModule extends ReactContextBaseJavaModule{
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.switchVersion(hash);
try {
updateContext.switchVersion(hash);
} catch (Throwable err) {
Log.e("pushy", "switchVersionLater failed", err);
}
}
});
}
@@ -170,4 +182,10 @@ public class UpdateModule extends ReactContextBaseJavaModule{
}
});
}
/* 发送事件*/
public static void sendEvent(String eventName, WritableMap params) {
((ReactContext) mContext).getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName,
params);
}
}

0
docs/.nojekyll Normal file
View File

53
docs/README.md Normal file
View File

@@ -0,0 +1,53 @@
# react-native-update [![npm version](https://badge.fury.io/js/react-native-update.svg)](http://badge.fury.io/js/react-native-update)
本组件是面向 React Native 提供热更新功能的组件,请结合[Update 服务](https://update.reactnative.cn/)使用。
<details>
<summary>
注意,在 iOS 上使用热更新有被拒的可能。被拒之后可以按此步骤单独屏蔽 iOS 端(`react-native-update`版本需 >= 5.3.2)
</summary>
1. 如果 RN 版本>=0.60,在项目根目录下编辑或创建 react-native.config.js添加如下内容
```js
// react-native.config.js
module.exports = {
dependencies: {
'react-native-update': {
platforms: {
ios: null, // 阻止ios模块自动链接
},
},
},
};
```
2. 如果在原生代码端尚未配置,则跳过下面文档中的 ios 端的配置。如果已经配置,则按文档的步骤反向操作(添加的 ios 代码删去)。
3. 如果是 0.60 以上版本或使用了 cocoapods在 ios 目录中再次运行 pod install确保 Podfile 和 Podfile.lock 中都没有'react-native-update'。如果 RN 版本<0.60则运行`react-native unlink react-native-update`
4. js 代码里调用 checkUpdate()方法前判断 Platform.OS如果是 ios 平台则直接 return 跳过
</details>
### 优势
1. 命令行工具&网页双端管理,版本发布过程简单便捷,完全可以集成 CI。
2. 基于 bsdiff 算法创建的**超小更新包**,通常版本迭代后在 1-10KB 之间,避免数百 KB 的流量消耗。
3. 支持崩溃回滚,安全可靠。
4. meta 信息及开放 API提供更高扩展性。
5. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
### 本地开发
```
$ git clone git@github.com:reactnativecn/react-native-pushy.git
$ cd react-native-pushy/Example/testHotUpdate
$ yarn
$ yarn start
```
本地库文件使用 yarn link 链接,因此可直接在源文件中修改,在 testHotUpdate 项目中调试。
### 关于我们
本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。
关于此插件发现任何问题,可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)或者[中文社区](http://bbs.reactnative.cn/category/7)发帖提问。

10
docs/_sidebar.md Normal file
View File

@@ -0,0 +1,10 @@
- 安装与快速入门
- [准备工作](guide.md)
- [添加热更新功能](guide2.md)
- [发布版本](guide3.md)
- [常见问题与高级指南](faq_advance.md)
- [API接口](api.md)
- [命令行工具](cli.md)
- [最近更新](changelog.md)

View File

@@ -6,11 +6,11 @@
### packageVersion
当前应用包的版本
当前应用原生包的版本。其中android取自`versionName`字段(位于`android/build.gralde`中)。ios取自`CFBundleShortVersionString`字段(位于`ios/项目名/Info.plist`中)
### currentVersion
当前版本的Hash号。
当前热更新版本jsbundle文件的Hash号。
### isFirstTime
@@ -24,7 +24,7 @@
检查更新,返回值有三种情形:
1. `{expired: true}`:该应用包(原生部分)已过期,需要前往应用市场下载新的版本。
1. `{expired: true}`:该应用原生包已过期,需要前往应用市场下载新的版本。
```
{
expired: true,
@@ -35,7 +35,7 @@
3. `{update: true}`当前有新版本可以更新。info的`name``description`字段可
以用于提示用户,而`metaInfo`字段则可以根据你的需求自定义其它属性(如是否静默更新、
是否强制更新等等)。另外还有几个字段,包含了完整更新包或补丁包的下载地址,
是否强制更新等等)。另外还有几个字段,包含了热更新文件的下载地址,
```
{
update: true,
@@ -43,7 +43,6 @@
hash: 'hash',
description: '添加聊天功能\n修复商城页面BUG',
metaInfo: '{"silent":true}',
updateUrl: 'http://update-packages.reactnative.cn/hash',
pdiffUrl: 'http://update-packages.reactnative.cn/hash',
diffUrl: 'http://update-packages.reactnative.cn/hash',
}

View File

@@ -1,5 +1,39 @@
### 最近更新
## 5.5.8 (2020-04-02)
1. 提供setCustomInstanceManager方法方便自己集成RN的用户调用
## 5.5.6 (2020-02-11)
1. bundle时清除缓存
2. 修复更新包过大时可能出现的崩溃
3. publish时检查文件格式
## 5.5.5 (2020-01-18)
1. 提升服务健壮性
## 5.5.4 (2020-01-13)
1. 防止某些情况下安卓找不到bundle文件引起的崩溃
## 5.5.3 (2019-12-18)
1. 忽略hermes的输出避免buffer溢出
## 5.5.2 (2019-12-06)
1. 修复使用use_frameworks时无法读取时间戳的问题
## 5.5.0 (2019-11-24)
1. 打包时加入时间戳
## 5.4.0 (2019-11-16)
1. 修改类名方法名
## 5.3.2 (2019-10-25)
1. 可以禁用ios端以避免审核被拒

View File

@@ -4,7 +4,7 @@
```
$ npm install -g react-native-update-cli
$ npm install --save react-native-update
$ npm install react-native-update
```
## 使用
@@ -90,18 +90,18 @@ $ npm install --save react-native-update
#### pushy packages
查看已经上传的包。这项操作也可以在网页管理端进行。
查看已经上传的原生包。这项操作也可以在网页管理端进行。
- platform: ios|android 对应的平台
#### pushy publish <ppkFile>
发布新的更新版本。
发布新的更新版本ppk文件
- platform: ios|android 对应的平台
- name: 当前版本的名字(版本号)
- description: 当前版本的描述信息,可以对用户进行展示
- metaInfo: 当前版本的元信息,可以用来保存一些额外信息
- name: 当前热更新版本的名字(版本号)
- description: 当前热更新版本的描述信息,可以对用户进行展示
- metaInfo: 当前热更新版本的元信息,可以用来保存一些额外信息
#### pushy versions
@@ -111,8 +111,8 @@ $ npm install --save react-native-update
#### pushy update
为一个包版本绑定一个更新版本。这项操作也可以在网页管理端进行。
为一个原生包版本绑定一个更新版本。这项操作也可以在网页管理端进行。
- platform: ios|android 对应的平台
- versionId: 要绑定的版本 ID
- packageId: 要绑定的包 ID
- versionId: 要绑定的热更新版本 ID
- packageId: 要绑定的原生包 ID

View File

@@ -57,7 +57,7 @@
目前我们的热更新服务完全免费但限制每个账号不超过3个应用每个应用不超过10个活跃的包和100个活跃的热更新版本每个应用每个月不超过10000次下载。iOS和Android版本记做不同的应用。
已经移除的应用、包版本、热更新版本不在统计之列,所以你可以移除测试时产生的和已过期版本来更有效的利用空间。
已经移除的应用、原生包版本、热更新版本不在统计之列,所以你可以移除测试时产生的和已过期版本来更有效的利用空间。
我们会在将来推出付费的升级版本,针对用户量较大、版本迭代较快的用户提供扩容方案。如果您有急迫的需求,可以[联系我们](https://reactnative.cn/about.html#content)。

View File

@@ -52,7 +52,7 @@ npm i react-native-update@4.x
请记得一定要重新编译react-native run-ios或run-android命令编译或在Xcode/Android Studio中重新编译
## 一、手动link
## 手动link
如果RN版本 >= 0.60则可以跳过此步骤
@@ -74,10 +74,20 @@ pod 'react-native-update', path: '../node_modules/react-native-update'
<summary>RN < 0.60且不使用CocoaPods</summary>
1. 在XCode中的Project Navigator里,右键点击`Libraries``Add Files to [你的工程名]`
2. 进入`node_modules``react-native-update``ios 并选中 `RCTHotUpdate.xcodeproj`
3. 在XCode中的project navigator里,选中你的工程,在 `Build Phases``Link Binary With Libraries` 中添加 `libRCTHotUpdate.a`
2. 进入`node_modules``react-native-update``ios 并选中 `RCTPushy.xcodeproj`
3. 在XCode中的project navigator里,选中你的工程,在 `Build Phases``Link Binary With Libraries` 中添加 `libRCTPushy.a`
4. 继续在`Build Settings`里搜索`Header Search Path`,添加$(SRCROOT)/../node_modules/react-native-update/ios
5. 重新编译
5. `Build Phases`添加一个`New Run Script Phase`运行脚本,内容如下
```
#!/bin/bash
set -x
DEST="../node_modules/react-native-update/ios/"
date +%s > "$DEST/pushy_build_time.txt"
```
编译的时候就会在`../node_modules/react-native-update/ios/`文件夹下面生成一个`pushy_build_time.txt`文件。
然后在`Copy Bundle Resources`里把生成的`pushy_build_time.txt`文件添加进去。
6. 重新编译
</details>
@@ -102,7 +112,7 @@ pod 'react-native-update', path: '../node_modules/react-native-update'
- 在`getPackages()` 方法中增加 `new UpdatePackage()`(注意上一行可能要增加一个逗号)
</details>
## 二、配置Bundle URL
## 配置Bundle URL
注意此步骤无论任何版本,目前都需要手动配置。
@@ -115,7 +125,7 @@ pod 'react-native-update', path: '../node_modules/react-native-update'
```objective-c
// ... 其它代码
#import "RCTHotUpdate.h"
#import "RCTPushy.h"
// 如果RN版本 >= 0.59修改sourceURLForBridge
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
@@ -124,7 +134,7 @@ pod 'react-native-update', path: '../node_modules/react-native-update'
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
#else
 // 非DEBUG情况下替换为热更新bundle
return [RCTHotUpdate bundleURL];
return [RCTPushy bundleURL];
#endif
}
@@ -136,7 +146,7 @@ pod 'react-native-update', path: '../node_modules/react-native-update'
 jsCodeLocation = ..........
#else
 // 非DEBUG情况下替换为热更新bundle
 jsCodeLocation = [RCTHotUpdate bundleURL];
 jsCodeLocation = [RCTPushy bundleURL];
#endif
// ... 其它代码
}
@@ -164,7 +174,7 @@ public class MainApplication extends Application implements ReactApplication {
}
```
## 三、登录与创建应用
## 登录与创建应用
首先请在<https://update.reactnative.cn>注册帐号,然后在你的项目根目录下运行以下命令:

View File

@@ -25,7 +25,7 @@ const info = await checkUpdate(appKey)
返回的info有三种情况
1. `{expired: true}`:该应用包(原生部分)已过期,需要前往应用市场下载新的版本
1. `{expired: true}`:该应用原生包已过期已从pushy服务器中删除开发者应该在pushy控制台添加一个更新下载链接并自行提示用户下载
2. `{upToDate: true}`:当前已经更新到最新,无需进行更新。
@@ -36,7 +36,7 @@ react-native-update会首先尝试耗费流量更少的更新方式。将info对
## 切换版本
downloadUpdate的返回值是一个hash字符串它是当前版本的唯一标识。
downloadUpdate的返回值是一个hash字符串它是当前热更新版本的唯一标识。
你可以使用`switchVersion`函数立即切换版本(此时应用会立即重新加载),或者选择调用
`switchVersionLater`,让应用在下一次启动的时候再加载新的版本。
@@ -139,8 +139,8 @@ class MyProject extends Component {
</Text>
<Text style={styles.instructions}>
这是版本一 {'\n'}
当前包版本号: {packageVersion}{'\n'}
当前版本Hash: {currentVersion||'(空)'}{'\n'}
当前原生包版本号: {packageVersion}{'\n'}
当前热更新版本Hash: {currentVersion||'(空)'}{'\n'}
</Text>
<TouchableOpacity onPress={this.checkUpdate}>
<Text style={styles.instructions}>

View File

@@ -16,7 +16,7 @@
$ pushy uploadIpa <your-package.ipa>
```
即可上传ipa以供后续版本比对之用。
即可上传ipa以供后续版本比对之用。此ipa的`CFBundleShortVersionString`字段(位于`ios/项目名/Info.plist`中)会被记录为原生版本号`packageVersion`
随后你可以选择往AppStore发布这个版本也可以先通过Test flight等方法进行测试。
@@ -31,7 +31,7 @@ $ pushy uploadIpa <your-package.ipa>
$ pushy uploadApk android/app/build/outputs/apk/release/app-release.apk
```
即可上传apk以供后续版本比对之用。
即可上传apk以供后续版本比对之用。此apk的`versionName`字段(位于`android/build.gralde`中)会被记录为原生版本号`packageVersion`
随后你可以选择往应用市场发布这个版本也可以先往设备上直接安装这个apk文件以进行测试。
@@ -51,17 +51,17 @@ Would you like to publish it?(Y/N)
```
Uploading [========================================================] 100% 0.0s
Enter version name: <输入版本名字如1.0.0-rc>
Enter description: <输入版本描述>
Enter version name: <输入热更新版本名字如1.0.0-rc>
Enter description: <输入热更新版本描述>
Enter meta info: {"ok":1}
Ok.
Would you like to bind packages to this version?(Y/N)
```
此时版本已经提交到update服务但用户暂时看不到此更新你需要先将特定的包版本绑定到此热更新版本上。
此时版本已经提交到update服务但用户暂时看不到此更新你需要先将特定的原生包版本绑定到此热更新版本上。
此时输入Y立即绑定你也可以在将来使用`pushy update --platform <ios|android>`来使得对应包版本的用户更新。
除此以外,你还可以在网页端操作,简单的将对应的包版本拖到此版本下即可。
此时输入Y立即绑定你也可以在将来使用`pushy update --platform <ios|android>`来使得对应原生包版本的用户更新。
除此以外,你还可以在网页端操作,简单的将对应的原生包版本拖到此热更新版本下即可。
```
Offset 0
@@ -72,7 +72,7 @@ Enter versionId or page Up/page Down/Begin(U/D/B) <输入序号,U/D翻页,B回
1) 1.0(normal) - 3 FiWYm9lB (未命名)
Total 1 packages.
Enter packageId: <输入包版本序号,序号就是上面列表中)前面的数字>
Enter packageId: <输入原生包版本序号,序号就是上面列表中)前面的数字>
```
版本绑定完毕后,客户端就应当可以检查到更新并进行更新了。

25
docs/index.html Normal file
View File

@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>react-native-update - react-native hot update</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="description" content="react-native hot update">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify/lib/themes/vue.css">
</head>
<body>
<div id="app"></div>
<script>
window.$docsify = {
name: 'react-native-update',
repo: 'https://github.com/reactnativecn/react-native-pushy',
formatUpdated: '{MM}/{DD} {HH}:{mm}',
loadSidebar: true,
subMaxLevel: 2,
auto2top: true
}
</script>
<script src="//cdn.jsdelivr.net/npm/docsify/lib/docsify.min.js"></script>
</body>
</html>

1
domains.json Normal file
View File

@@ -0,0 +1 @@
["update.react-native.cn", "update.reactnative.cn"]

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>RCTHotUpdate.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>22</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>91C5EFFE1C76ECA90037E727</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@@ -1,80 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "91C5EFFE1C76ECA90037E727"
BuildableName = "libRCTHotUpdate.a"
BlueprintName = "RCTHotUpdate"
ReferencedContainer = "container:RCTHotUpdate.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>RCTHotUpdate.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>91C5EFFE1C76ECA90037E727</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@@ -7,12 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
91C5F0031C76ECA90037E727 /* RCTHotUpdate.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 91C5F0021C76ECA90037E727 /* RCTHotUpdate.h */; };
91C5F0051C76ECA90037E727 /* RCTHotUpdate.m in Sources */ = {isa = PBXBuildFile; fileRef = 91C5F0041C76ECA90037E727 /* RCTHotUpdate.m */; };
9F1BCB1D1CAE5937000EF2CB /* RCTHotUpdateManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB1C1CAE5937000EF2CB /* RCTHotUpdateManager.m */; };
91C5F0031C76ECA90037E727 /* RCTPushy.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 91C5F0021C76ECA90037E727 /* RCTPushy.h */; };
91C5F0051C76ECA90037E727 /* RCTPushy.m in Sources */ = {isa = PBXBuildFile; fileRef = 91C5F0041C76ECA90037E727 /* RCTPushy.m */; };
9F1BCB1D1CAE5937000EF2CB /* RCTPushyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */; };
9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */; };
9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */; };
9F292F7D1C7C44290095945D /* RCTHotUpdateDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F292F7C1C7C44290095945D /* RCTHotUpdateDownloader.m */; };
9F292F7D1C7C44290095945D /* RCTPushyDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */; };
9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5B1C7C25DC00C794C0 /* aescrypt.c */; };
9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5C1C7C25DC00C794C0 /* aeskey.c */; };
9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5E1C7C25DC00C794C0 /* aestab.c */; };
@@ -36,24 +36,24 @@
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
91C5F0031C76ECA90037E727 /* RCTHotUpdate.h in CopyFiles */,
91C5F0031C76ECA90037E727 /* RCTPushy.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
91C5EFFF1C76ECA90037E727 /* libRCTHotUpdate.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTHotUpdate.a; sourceTree = BUILT_PRODUCTS_DIR; };
91C5F0021C76ECA90037E727 /* RCTHotUpdate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTHotUpdate.h; sourceTree = "<group>"; };
91C5F0041C76ECA90037E727 /* RCTHotUpdate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTHotUpdate.m; sourceTree = "<group>"; };
9F1BCB1B1CAE5937000EF2CB /* RCTHotUpdateManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHotUpdateManager.h; sourceTree = "<group>"; };
9F1BCB1C1CAE5937000EF2CB /* RCTHotUpdateManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTHotUpdateManager.m; sourceTree = "<group>"; };
91C5EFFF1C76ECA90037E727 /* libRCTPushy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTPushy.a; sourceTree = BUILT_PRODUCTS_DIR; };
91C5F0021C76ECA90037E727 /* RCTPushy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTPushy.h; sourceTree = "<group>"; };
91C5F0041C76ECA90037E727 /* RCTPushy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTPushy.m; sourceTree = "<group>"; };
9F1BCB1B1CAE5937000EF2CB /* RCTPushyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPushyManager.h; sourceTree = "<group>"; };
9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPushyManager.m; sourceTree = "<group>"; };
9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bspatch.c; sourceTree = "<group>"; };
9F1BCB3C1CAF6B3E000EF2CB /* bspatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bspatch.h; sourceTree = "<group>"; };
9F1BCB4D1CAF6B68000EF2CB /* BSDiff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSDiff.h; sourceTree = "<group>"; };
9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSDiff.m; sourceTree = "<group>"; };
9F292F7B1C7C44290095945D /* RCTHotUpdateDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHotUpdateDownloader.h; sourceTree = "<group>"; };
9F292F7C1C7C44290095945D /* RCTHotUpdateDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTHotUpdateDownloader.m; sourceTree = "<group>"; };
9F292F7B1C7C44290095945D /* RCTPushyDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPushyDownloader.h; sourceTree = "<group>"; };
9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPushyDownloader.m; sourceTree = "<group>"; };
9F394D591C7C25DC00C794C0 /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = "<group>"; };
9F394D5A1C7C25DC00C794C0 /* aes_via_ace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes_via_ace.h; sourceTree = "<group>"; };
9F394D5B1C7C25DC00C794C0 /* aescrypt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aescrypt.c; sourceTree = "<group>"; };
@@ -105,7 +105,7 @@
91C5EFF61C76ECA90037E727 = {
isa = PBXGroup;
children = (
91C5F0011C76ECA90037E727 /* RCTHotUpdate */,
91C5F0011C76ECA90037E727 /* RCTPushy */,
91C5F0001C76ECA90037E727 /* Products */,
);
sourceTree = "<group>";
@@ -113,24 +113,24 @@
91C5F0001C76ECA90037E727 /* Products */ = {
isa = PBXGroup;
children = (
91C5EFFF1C76ECA90037E727 /* libRCTHotUpdate.a */,
91C5EFFF1C76ECA90037E727 /* libRCTPushy.a */,
);
name = Products;
sourceTree = "<group>";
};
91C5F0011C76ECA90037E727 /* RCTHotUpdate */ = {
91C5F0011C76ECA90037E727 /* RCTPushy */ = {
isa = PBXGroup;
children = (
9F1BCB381CAF6B3E000EF2CB /* BSDiff */,
9F394D571C7C25DC00C794C0 /* SSZipArchive */,
91C5F0021C76ECA90037E727 /* RCTHotUpdate.h */,
91C5F0041C76ECA90037E727 /* RCTHotUpdate.m */,
9F1BCB1B1CAE5937000EF2CB /* RCTHotUpdateManager.h */,
9F1BCB1C1CAE5937000EF2CB /* RCTHotUpdateManager.m */,
9F292F7B1C7C44290095945D /* RCTHotUpdateDownloader.h */,
9F292F7C1C7C44290095945D /* RCTHotUpdateDownloader.m */,
91C5F0021C76ECA90037E727 /* RCTPushy.h */,
91C5F0041C76ECA90037E727 /* RCTPushy.m */,
9F1BCB1B1CAE5937000EF2CB /* RCTPushyManager.h */,
9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */,
9F292F7B1C7C44290095945D /* RCTPushyDownloader.h */,
9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */,
);
path = RCTHotUpdate;
path = RCTPushy;
sourceTree = "<group>";
};
9F1BCB381CAF6B3E000EF2CB /* BSDiff */ = {
@@ -213,9 +213,9 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
91C5EFFE1C76ECA90037E727 /* RCTHotUpdate */ = {
91C5EFFE1C76ECA90037E727 /* RCTPushy */ = {
isa = PBXNativeTarget;
buildConfigurationList = 91C5F0081C76ECA90037E727 /* Build configuration list for PBXNativeTarget "RCTHotUpdate" */;
buildConfigurationList = 91C5F0081C76ECA90037E727 /* Build configuration list for PBXNativeTarget "RCTPushy" */;
buildPhases = (
91C5EFFB1C76ECA90037E727 /* Sources */,
91C5EFFC1C76ECA90037E727 /* Frameworks */,
@@ -225,9 +225,9 @@
);
dependencies = (
);
name = RCTHotUpdate;
productName = RCTHotUpdate;
productReference = 91C5EFFF1C76ECA90037E727 /* libRCTHotUpdate.a */;
name = RCTPushy;
productName = RCTPushy;
productReference = 91C5EFFF1C76ECA90037E727 /* libRCTPushy.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
@@ -244,11 +244,12 @@
};
};
};
buildConfigurationList = 91C5EFFA1C76ECA90037E727 /* Build configuration list for PBXProject "RCTHotUpdate" */;
buildConfigurationList = 91C5EFFA1C76ECA90037E727 /* Build configuration list for PBXProject "RCTPushy" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
);
mainGroup = 91C5EFF61C76ECA90037E727;
@@ -256,7 +257,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
91C5EFFE1C76ECA90037E727 /* RCTHotUpdate */,
91C5EFFE1C76ECA90037E727 /* RCTPushy */,
);
};
/* End PBXProject section */
@@ -271,20 +272,20 @@
9F394D871C7C25DC00C794C0 /* mztools.c in Sources */,
9F394D821C7C25DC00C794C0 /* hmac.c in Sources */,
9F394D881C7C25DC00C794C0 /* unzip.c in Sources */,
9F1BCB1D1CAE5937000EF2CB /* RCTHotUpdateManager.m in Sources */,
9F1BCB1D1CAE5937000EF2CB /* RCTPushyManager.m in Sources */,
9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */,
9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */,
9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */,
9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */,
9F394D801C7C25DC00C794C0 /* entropy.c in Sources */,
9F292F7D1C7C44290095945D /* RCTHotUpdateDownloader.m in Sources */,
9F292F7D1C7C44290095945D /* RCTPushyDownloader.m in Sources */,
9F394D831C7C25DC00C794C0 /* prng.c in Sources */,
9F394D861C7C25DC00C794C0 /* ioapi.c in Sources */,
9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */,
9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */,
9F394D891C7C25DC00C794C0 /* zip.c in Sources */,
9F394D841C7C25DC00C794C0 /* pwd2key.c in Sources */,
91C5F0051C76ECA90037E727 /* RCTHotUpdate.m in Sources */,
91C5F0051C76ECA90037E727 /* RCTPushy.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -400,7 +401,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
91C5EFFA1C76ECA90037E727 /* Build configuration list for PBXProject "RCTHotUpdate" */ = {
91C5EFFA1C76ECA90037E727 /* Build configuration list for PBXProject "RCTPushy" */ = {
isa = XCConfigurationList;
buildConfigurations = (
91C5F0061C76ECA90037E727 /* Debug */,
@@ -409,7 +410,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
91C5F0081C76ECA90037E727 /* Build configuration list for PBXNativeTarget "RCTHotUpdate" */ = {
91C5F0081C76ECA90037E727 /* Build configuration list for PBXNativeTarget "RCTPushy" */ = {
isa = XCConfigurationList;
buildConfigurations = (
91C5F0091C76ECA90037E727 /* Debug */,

View File

@@ -1,6 +1,6 @@
//
// BSDiff.h
// RCTHotUpdate
// RCTPushy
//
// Created by lvbingru on 16/4/2.
// Copyright © 2016年 erica. All rights reserved.

View File

@@ -1,6 +1,6 @@
//
// BSDiff.m
// RCTHotUpdate
// RCTPushy
//
// Created by lvbingru on 16/4/2.
// Copyright © 2016 erica. All rights reserved.

View File

@@ -1,6 +1,6 @@
//
// RCTHotUpdate.h
// RCTHotUpdate
// RCTPushy.h
// RCTPushy
//
// Created by LvBingru on 2/19/16.
// Copyright © 2016 erica. All rights reserved.
@@ -12,7 +12,7 @@
#import "RCTBridgeModule.h"
#endif
@interface RCTHotUpdate : NSObject<RCTBridgeModule>
@interface RCTPushy : NSObject<RCTBridgeModule>
+ (NSURL *)bundleURL;

View File

@@ -1,14 +1,14 @@
//
// RCTHotUpdate.m
// RCTHotUpdate
// RCTPushy.m
// RCTPushy
//
// Created by LvBingru on 2/19/16.
// Copyright © 2016 erica. All rights reserved.
//
#import "RCTHotUpdate.h"
#import "RCTHotUpdateDownloader.h"
#import "RCTHotUpdateManager.h"
#import "RCTPushy.h"
#import "RCTPushyDownloader.h"
#import "RCTPushyManager.h"
#if __has_include(<React/RCTBridge.h>)
#import "React/RCTEventDispatcher.h"
@@ -21,15 +21,15 @@
#endif
//
static NSString *const keyUpdateInfo = @"REACTNATIVECN_HOTUPDATE_INFO_KEY";
static NSString *const keyPushyInfo = @"REACTNATIVECN_PUSHY_INFO_KEY";
static NSString *const paramPackageVersion = @"packageVersion";
static NSString *const paramLastVersion = @"lastVersion";
static NSString *const paramCurrentVersion = @"currentVersion";
static NSString *const paramIsFirstTime = @"isFirstTime";
static NSString *const paramIsFirstLoadOk = @"isFirstLoadOK";
static NSString *const keyFirstLoadMarked = @"REACTNATIVECN_HOTUPDATE_FIRSTLOADMARKED_KEY";
static NSString *const keyRolledBackMarked = @"REACTNATIVECN_HOTUPDATE_ROLLEDBACKMARKED_KEY";
static NSString *const KeyPackageUpdatedMarked = @"REACTNATIVECN_HOTUPDATE_ISPACKAGEUPDATEDMARKED_KEY";
static NSString *const keyFirstLoadMarked = @"REACTNATIVECN_PUSHY_FIRSTLOADMARKED_KEY";
static NSString *const keyRolledBackMarked = @"REACTNATIVECN_PUSHY_ROLLEDBACKMARKED_KEY";
static NSString *const KeyPackageUpdatedMarked = @"REACTNATIVECN_PUSHY_ISPACKAGEUPDATEDMARKED_KEY";
// app info
static NSString * const AppVersionKey = @"appVersion";
@@ -46,52 +46,52 @@ static NSString * const ERROR_BSDIFF = @"bsdiff error";
static NSString * const ERROR_FILE_OPERATION = @"file operation error";
// event def
static NSString * const EVENT_PROGRESS_DOWNLOAD = @"RCTHotUpdateDownloadProgress";
static NSString * const EVENT_PROGRESS_UNZIP = @"RCTHotUpdateUnzipProgress";
static NSString * const EVENT_PROGRESS_DOWNLOAD = @"RCTPushyDownloadProgress";
static NSString * const EVENT_PROGRESS_UNZIP = @"RCTPushyUnzipProgress";
static NSString * const PARAM_PROGRESS_HASHNAME = @"hashname";
static NSString * const PARAM_PROGRESS_RECEIVED = @"received";
static NSString * const PARAM_PROGRESS_TOTAL = @"total";
typedef NS_ENUM(NSInteger, HotUpdateType) {
HotUpdateTypeFullDownload = 1,
HotUpdateTypePatchFromPackage = 2,
HotUpdateTypePatchFromPpk = 3,
typedef NS_ENUM(NSInteger, PushyType) {
PushyTypeFullDownload = 1,
PushyTypePatchFromPackage = 2,
PushyTypePatchFromPpk = 3,
};
static BOOL ignoreRollback = false;
@implementation RCTHotUpdate {
RCTHotUpdateManager *_fileManager;
@implementation RCTPushy {
RCTPushyManager *_fileManager;
}
@synthesize bridge = _bridge;
@synthesize methodQueue = _methodQueue;
RCT_EXPORT_MODULE(RCTHotUpdate);
RCT_EXPORT_MODULE(RCTPushy);
+ (NSURL *)bundleURL
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *updateInfo = [defaults dictionaryForKey:keyUpdateInfo];
if (updateInfo) {
NSString *curPackageVersion = [RCTHotUpdate packageVersion];
NSString *packageVersion = [updateInfo objectForKey:paramPackageVersion];
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
if (pushyInfo) {
NSString *curPackageVersion = [RCTPushy packageVersion];
NSString *packageVersion = [pushyInfo objectForKey:paramPackageVersion];
BOOL needClearUpdateInfo = ![curPackageVersion isEqualToString:packageVersion];
if (needClearUpdateInfo) {
[defaults setObject:nil forKey:keyUpdateInfo];
BOOL needClearPushyInfo = ![curPackageVersion isEqualToString:packageVersion];
if (needClearPushyInfo) {
[defaults setObject:nil forKey:keyPushyInfo];
[defaults setObject:@(YES) forKey:KeyPackageUpdatedMarked];
[defaults synchronize];
// ...need clear files later
}
else {
NSString *curVersion = updateInfo[paramCurrentVersion];
NSString *lastVersion = updateInfo[paramLastVersion];
NSString *curVersion = pushyInfo[paramCurrentVersion];
NSString *lastVersion = pushyInfo[paramLastVersion];
BOOL isFirstTime = [updateInfo[paramIsFirstTime] boolValue];
BOOL isFirstLoadOK = [updateInfo[paramIsFirstLoadOk] boolValue];
BOOL isFirstTime = [pushyInfo[paramIsFirstTime] boolValue];
BOOL isFirstLoadOK = [pushyInfo[paramIsFirstLoadOk] boolValue];
NSString *loadVersioin = curVersion;
BOOL needRollback = (!ignoreRollback && isFirstTime == NO && isFirstLoadOK == NO) || loadVersioin.length<=0;
@@ -104,11 +104,11 @@ RCT_EXPORT_MODULE(RCTHotUpdate);
paramIsFirstTime:@(NO),
paramIsFirstLoadOk:@(YES),
paramPackageVersion:curPackageVersion}
forKey:keyUpdateInfo];
forKey:keyPushyInfo];
}
else {
// roll back to bundle
[defaults setObject:nil forKey:keyUpdateInfo];
[defaults setObject:nil forKey:keyPushyInfo];
}
[defaults setObject:@(YES) forKey:keyRolledBackMarked];
[defaults synchronize];
@@ -118,15 +118,15 @@ RCT_EXPORT_MODULE(RCTHotUpdate);
// bundleURL may be called many times, ignore rollbacks before process restarted again.
ignoreRollback = true;
NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:updateInfo];
NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] initWithDictionary:pushyInfo];
newInfo[paramIsFirstTime] = @(NO);
[defaults setObject:newInfo forKey:keyUpdateInfo];
[defaults setObject:newInfo forKey:keyPushyInfo];
[defaults setObject:@(YES) forKey:keyFirstLoadMarked];
[defaults synchronize];
}
if (loadVersioin.length) {
NSString *downloadDir = [RCTHotUpdate downloadDir];
NSString *downloadDir = [RCTPushy downloadDir];
NSString *bundlePath = [[downloadDir stringByAppendingPathComponent:loadVersioin] stringByAppendingPathComponent:BUNDLE_FILE_NAME];
if ([[NSFileManager defaultManager] fileExistsAtPath:bundlePath isDirectory:NULL]) {
@@ -137,7 +137,7 @@ RCT_EXPORT_MODULE(RCTHotUpdate);
}
}
return [RCTHotUpdate binaryBundleURL];
return [RCTPushy binaryBundleURL];
}
+ (BOOL)requiresMainQueueSetup {
@@ -150,13 +150,13 @@ RCT_EXPORT_MODULE(RCTHotUpdate);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *ret = [NSMutableDictionary new];
ret[@"downloadRootDir"] = [RCTHotUpdate downloadDir];
ret[@"packageVersion"] = [RCTHotUpdate packageVersion];
ret[@"buildTime"] = [RCTHotUpdate buildTime];
ret[@"downloadRootDir"] = [RCTPushy downloadDir];
ret[@"packageVersion"] = [RCTPushy packageVersion];
ret[@"buildTime"] = [RCTPushy buildTime];
ret[@"isRolledBack"] = [defaults objectForKey:keyRolledBackMarked];
ret[@"isFirstTime"] = [defaults objectForKey:keyFirstLoadMarked];
NSDictionary *updateInfo = [defaults dictionaryForKey:keyUpdateInfo];
ret[@"currentVersion"] = [updateInfo objectForKey:paramCurrentVersion];
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
ret[@"currentVersion"] = [pushyInfo objectForKey:paramCurrentVersion];
// clear isFirstTimemarked
if ([[defaults objectForKey:keyFirstLoadMarked] boolValue]) {
@@ -183,7 +183,7 @@ RCT_EXPORT_MODULE(RCTHotUpdate);
{
self = [super init];
if (self) {
_fileManager = [RCTHotUpdateManager new];
_fileManager = [RCTPushyManager new];
}
return self;
}
@@ -192,7 +192,7 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[self hotUpdate:HotUpdateTypeFullDownload options:options callback:^(NSError *error) {
[self doPushy:PushyTypeFullDownload options:options callback:^(NSError *error) {
if (error) {
reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
}
@@ -206,7 +206,7 @@ RCT_EXPORT_METHOD(downloadPatchFromPackage:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[self hotUpdate:HotUpdateTypePatchFromPackage options:options callback:^(NSError *error) {
[self doPushy:PushyTypePatchFromPackage options:options callback:^(NSError *error) {
if (error) {
reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
}
@@ -220,7 +220,7 @@ RCT_EXPORT_METHOD(downloadPatchFromPpk:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
[self hotUpdate:HotUpdateTypePatchFromPpk options:options callback:^(NSError *error) {
[self doPushy:PushyTypePatchFromPpk options:options callback:^(NSError *error) {
if (error) {
reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error);
}
@@ -236,9 +236,9 @@ RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options)
if (hashName.length) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *lastVersion = nil;
if ([defaults objectForKey:keyUpdateInfo]) {
NSDictionary *updateInfo = [defaults objectForKey:keyUpdateInfo];
lastVersion = updateInfo[paramCurrentVersion];
if ([defaults objectForKey:keyPushyInfo]) {
NSDictionary *pushyInfo = [defaults objectForKey:keyPushyInfo];
lastVersion = pushyInfo[paramCurrentVersion];
}
NSMutableDictionary *newInfo = [[NSMutableDictionary alloc] init];
@@ -246,8 +246,8 @@ RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options)
newInfo[paramLastVersion] = lastVersion;
newInfo[paramIsFirstTime] = @(YES);
newInfo[paramIsFirstLoadOk] = @(NO);
newInfo[paramPackageVersion] = [RCTHotUpdate packageVersion];
[defaults setObject:newInfo forKey:keyUpdateInfo];
newInfo[paramPackageVersion] = [RCTPushy packageVersion];
[defaults setObject:newInfo forKey:keyPushyInfo];
[defaults synchronize];
}
@@ -269,12 +269,12 @@ RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options)
RCT_EXPORT_METHOD(markSuccess)
{
// update package info
// up package info
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *packageInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:keyUpdateInfo]];
NSMutableDictionary *packageInfo = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:keyPushyInfo]];
[packageInfo setObject:@(NO) forKey:paramIsFirstTime];
[packageInfo setObject:@(YES) forKey:paramIsFirstLoadOk];
[defaults setObject:packageInfo forKey:keyUpdateInfo];
[defaults setObject:packageInfo forKey:keyPushyInfo];
[defaults synchronize];
// clear other package dir
@@ -282,7 +282,7 @@ RCT_EXPORT_METHOD(markSuccess)
}
#pragma mark - private
- (void)hotUpdate:(HotUpdateType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback
- (void)doPushy:(PushyType)type options:(NSDictionary *)options callback:(void (^)(NSError *error))callback
{
NSString *updateUrl = [RCTConvert NSString:options[@"updateUrl"]];
NSString *hashName = [RCTConvert NSString:options[@"hashName"]];
@@ -291,12 +291,12 @@ RCT_EXPORT_METHOD(markSuccess)
return;
}
NSString *originHashName = [RCTConvert NSString:options[@"originHashName"]];
if (type == HotUpdateTypePatchFromPpk && originHashName<=0) {
if (type == PushyTypePatchFromPpk && originHashName<=0) {
callback([self errorWithMessage:ERROR_OPTIONS]);
return;
}
NSString *dir = [RCTHotUpdate downloadDir];
NSString *dir = [RCTPushy downloadDir];
BOOL success = [_fileManager createDir:dir];
if (!success) {
callback([self errorWithMessage:ERROR_FILE_OPERATION]);
@@ -306,8 +306,8 @@ RCT_EXPORT_METHOD(markSuccess)
NSString *zipFilePath = [dir stringByAppendingPathComponent:[NSString stringWithFormat:@"%@%@",hashName, [self zipExtension:type]]];
NSString *unzipDir = [dir stringByAppendingPathComponent:hashName];
RCTLogInfo(@"RNUpdate -- download file %@", updateUrl);
[RCTHotUpdateDownloader download:updateUrl savePath:zipFilePath progressHandler:^(long long receivedBytes, long long totalBytes) {
RCTLogInfo(@"RCTPushy -- download file %@", updateUrl);
[RCTPushyDownloader download:updateUrl savePath:zipFilePath progressHandler:^(long long receivedBytes, long long totalBytes) {
[self.bridge.eventDispatcher sendAppEventWithName:EVENT_PROGRESS_DOWNLOAD
body:@{
PARAM_PROGRESS_HASHNAME:hashName,
@@ -319,7 +319,7 @@ RCT_EXPORT_METHOD(markSuccess)
callback(error);
}
else {
RCTLogInfo(@"RNUpdate -- unzip file %@", zipFilePath);
RCTLogInfo(@"RCTPushy -- unzip file %@", zipFilePath);
NSString *unzipFilePath = [dir stringByAppendingPathComponent:hashName];
[_fileManager unzipFileAtPath:zipFilePath toDestination:unzipFilePath progressHandler:^(NSString *entry,long entryNumber, long total) {
[self.bridge.eventDispatcher sendAppEventWithName:EVENT_PROGRESS_UNZIP
@@ -336,14 +336,14 @@ RCT_EXPORT_METHOD(markSuccess)
}
else {
switch (type) {
case HotUpdateTypePatchFromPackage:
case PushyTypePatchFromPackage:
{
NSString *sourceOrigin = [[NSBundle mainBundle] resourcePath];
NSString *bundleOrigin = [[RCTHotUpdate binaryBundleURL] path];
NSString *bundleOrigin = [[RCTPushy binaryBundleURL] path];
[self patch:hashName fromBundle:bundleOrigin source:sourceOrigin callback:callback];
}
break;
case HotUpdateTypePatchFromPpk:
case PushyTypePatchFromPpk:
{
NSString *lastVertionDir = [dir stringByAppendingPathComponent:originHashName];
@@ -365,7 +365,7 @@ RCT_EXPORT_METHOD(markSuccess)
- (void)patch:(NSString *)hashName fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin callback:(void (^)(NSError *error))callback
{
NSString *unzipDir = [[RCTHotUpdate downloadDir] stringByAppendingPathComponent:hashName];
NSString *unzipDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:hashName];
NSString *sourcePatch = [unzipDir stringByAppendingPathComponent:SOURCE_PATCH_NAME];
NSString *bundlePatch = [unzipDir stringByAppendingPathComponent:BUNDLE_PATCH_NAME];
@@ -401,10 +401,10 @@ RCT_EXPORT_METHOD(markSuccess)
- (void)clearInvalidFiles
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *updateInfo = [defaults objectForKey:keyUpdateInfo];
NSString *curVersion = [updateInfo objectForKey:paramCurrentVersion];
NSDictionary *pushyInfo = [defaults objectForKey:keyPushyInfo];
NSString *curVersion = [pushyInfo objectForKey:paramCurrentVersion];
NSString *downloadDir = [RCTHotUpdate downloadDir];
NSString *downloadDir = [RCTPushy downloadDir];
NSError *error = nil;
NSArray *list = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:downloadDir error:&error];
if (error) {
@@ -418,14 +418,14 @@ RCT_EXPORT_METHOD(markSuccess)
}
}
- (NSString *)zipExtension:(HotUpdateType)type
- (NSString *)zipExtension:(PushyType)type
{
switch (type) {
case HotUpdateTypeFullDownload:
case PushyTypeFullDownload:
return @".ppk";
case HotUpdateTypePatchFromPackage:
return @".apk.patch";
case HotUpdateTypePatchFromPpk:
case PushyTypePatchFromPackage:
return @".ipa.patch";
case PushyTypePatchFromPpk:
return @".ppk.patch";
default:
break;
@@ -434,7 +434,7 @@ RCT_EXPORT_METHOD(markSuccess)
- (NSError *)errorWithMessage:(NSString *)errorMessage
{
return [NSError errorWithDomain:@"cn.reactnative.hotupdate"
return [NSError errorWithDomain:@"cn.reactnative.pushy"
code:-1
userInfo:@{ NSLocalizedDescriptionKey: errorMessage}];
}
@@ -442,7 +442,7 @@ RCT_EXPORT_METHOD(markSuccess)
+ (NSString *)downloadDir
{
NSString *directory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject];
NSString *downloadDir = [directory stringByAppendingPathComponent:@"reactnativecnhotupdate"];
NSString *downloadDir = [directory stringByAppendingPathComponent:@"rctpushy"];
return downloadDir;
}

View File

@@ -1,6 +1,6 @@
//
// RCTHotUpdateDownloader.h
// RCTHotUpdate
// RCTPushyDownloader.h
// RCTPushy
//
// Created by lvbingru on 16/2/23.
// Copyright © 2016年 erica. All rights reserved.
@@ -8,7 +8,7 @@
#import <Foundation/Foundation.h>
@interface RCTHotUpdateDownloader : NSObject
@interface RCTPushyDownloader : NSObject
+ (void)download:(NSString *)downloadPath savePath:(NSString *)savePath
progressHandler:(void (^)(long long, long long))progressHandler

View File

@@ -1,21 +1,21 @@
//
// RCTHotUpdateDownloader.m
// RCTHotUpdate
// RCTPushyDownloader.m
// RCTPushy
//
// Created by lvbingru on 16/2/23.
// Copyright © 2016 erica. All rights reserved.
//
#import "RCTHotUpdateDownloader.h"
#import "RCTPushyDownloader.h"
@interface RCTHotUpdateDownloader()<NSURLSessionDelegate>
@interface RCTPushyDownloader()<NSURLSessionDelegate>
@property (copy) void (^progressHandler)(long long, long long);
@property (copy) void (^completionHandler)(NSString*, NSError*);
@property (copy) NSString *savePath;
@end
@implementation RCTHotUpdateDownloader
@implementation RCTPushyDownloader
+ (void)download:(NSString *)downloadPath savePath:(NSString *)savePath
progressHandler:(void (^)(long long receivedBytes, long long totalBytes))progressHandler
@@ -24,7 +24,7 @@ completionHandler:(void (^)(NSString *path, NSError *error))completionHandler
NSAssert(downloadPath, @"no download path");
NSAssert(savePath, @"no save path");
RCTHotUpdateDownloader *downloader = [RCTHotUpdateDownloader new];
RCTPushyDownloader *downloader = [RCTPushyDownloader new];
downloader.progressHandler = progressHandler;
downloader.completionHandler = completionHandler;
downloader.savePath = savePath;

View File

@@ -1,6 +1,6 @@
//
// RCTHotUpdateManager.h
// RCTHotUpdate
// RCTPushyManager.h
// RCTPushy
//
// Created by lvbingru on 16/4/1.
// Copyright © 2016年 erica. All rights reserved.
@@ -8,7 +8,7 @@
#import <Foundation/Foundation.h>
@interface RCTHotUpdateManager : NSObject
@interface RCTPushyManager : NSObject
- (BOOL)createDir:(NSString *)dir;

View File

@@ -1,17 +1,17 @@
//
// RCTHotUpdateManager.m
// RCTHotUpdate
// RCTPushyManager.m
// RCTPushy
//
// Created by lvbingru on 16/4/1.
// Copyright © 2016 erica. All rights reserved.
//
#import "RCTHotUpdateManager.h"
#import "RCTPushyManager.h"
#import "ZipArchive.h"
#import "BSDiff.h"
#import "bspatch.h"
@implementation RCTHotUpdateManager {
@implementation RCTPushyManager {
dispatch_queue_t _opQueue;
}
@@ -19,7 +19,7 @@
{
self = [super init];
if (self) {
_opQueue = dispatch_queue_create("cn.reactnative.hotupdate", DISPATCH_QUEUE_SERIAL);
_opQueue = dispatch_queue_create("cn.reactnative.pushy", DISPATCH_QUEUE_SERIAL);
}
return self;
}

View File

@@ -1 +1 @@
1570371472
1574665292

57
lib/getHost.js Normal file
View File

@@ -0,0 +1,57 @@
let availableDomain = 'update.react-native.cn';
function ping(domain, rejectImmediate) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = e => {
if (xhr.readyState !== 4) {
return;
}
if (xhr.status === 200) {
resolve(domain);
} else {
rejectImmediate ? reject() : setTimeout(reject, 5000);
}
};
xhr.open('HEAD', `https://${domain}`);
xhr.send();
xhr.timeout = 5000;
xhr.ontimeout = reject;
});
}
function logger(...args) {
// console.warn('pushy', ...args);
}
export async function tryBackupDomains() {
try {
await ping(availableDomain, true);
logger('main domain ok');
return;
} catch (e) {
logger('main domain failed');
}
let backupDomains = [];
try {
const resp = await fetch(
'https://cdn.jsdelivr.net/gh/reactnativecn/react-native-pushy@master/domains.json',
);
backupDomains = await resp.json();
logger('get remote domains:', backupDomains);
} catch (e) {
logger('get remote domains failed');
return;
}
const fastestDomain = await Promise.race(backupDomains.map(ping));
if (typeof fastestDomain === 'string') {
logger(`pick domain: ${fastestDomain}`);
availableDomain = fastestDomain;
} else {
logger('all remote domains failed');
}
}
export default function getHost() {
return `https://${availableDomain}/api`;
}

1
lib/index.d.ts vendored
View File

@@ -19,7 +19,6 @@ export interface UpdateAvailableResult {
hash: string;
description: string;
metaInfo: string;
updateUrl: string;
pdiffUrl: string;
diffUrl: string;
}

View File

@@ -1,14 +1,18 @@
import getHost, { tryBackupDomains } from './getHost';
import { NativeAppEventEmitter, NativeModules } from 'react-native';
const HotUpdate = NativeModules.HotUpdate || {};
const host = 'https://update.reactnative.cn/api';
let Pushy = NativeModules.Pushy;
export const downloadRootDir = HotUpdate.downloadRootDir;
export const packageVersion = HotUpdate.packageVersion;
export const currentVersion = HotUpdate.currentVersion;
export const isFirstTime = HotUpdate.isFirstTime;
export const isRolledBack = HotUpdate.isRolledBack;
export const buildTime = HotUpdate.buildTime;
if (!Pushy) {
throw new Error('react-native-update模块无法加载请对照安装文档检查配置。');
}
export const downloadRootDir = Pushy.downloadRootDir;
export const packageVersion = Pushy.packageVersion;
export const currentVersion = Pushy.currentVersion;
export const isFirstTime = Pushy.isFirstTime;
export const isRolledBack = Pushy.isRolledBack;
export const buildTime = Pushy.buildTime;
/*
Return json:
@@ -28,7 +32,6 @@ There is available update:
hash: 'hash',
description: '添加聊天功能\n修复商城页面BUG',
metaInfo: '{"silent":true}',
updateUrl: 'http://update-packages.reactnative.cn/hash',
pdiffUrl: 'http://update-packages.reactnative.cn/hash',
diffUrl: 'http://update-packages.reactnative.cn/hash',
}
@@ -40,20 +43,29 @@ function assertRelease() {
}
}
export async function checkUpdate(APPKEY) {
export async function checkUpdate(APPKEY, isRetry) {
assertRelease();
const resp = await fetch(`${host}/checkUpdate/${APPKEY}`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
packageVersion,
hash: currentVersion,
buildTime,
}),
});
let resp;
try {
resp = await fetch(`${getHost()}/checkUpdate/${APPKEY}`, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
packageVersion,
hash: currentVersion,
buildTime,
}),
});
} catch (e) {
if (isRetry) {
throw new Error('Could not connect to pushy server');
}
await tryBackupDomains();
return checkUpdate(APPKEY, true);
}
if (resp.status !== 200) {
throw new Error((await resp.json()).message);
@@ -69,18 +81,18 @@ export async function downloadUpdate(options) {
}
if (options.diffUrl) {
await HotUpdate.downloadPatchFromPpk({
await Pushy.downloadPatchFromPpk({
updateUrl: options.diffUrl,
hashName: options.hash,
originHashName: currentVersion,
});
} else if (options.pdiffUrl) {
await HotUpdate.downloadPatchFromPackage({
await Pushy.downloadPatchFromPackage({
updateUrl: options.pdiffUrl,
hashName: options.hash,
});
} else {
await HotUpdate.downloadUpdate({
await Pushy.downloadUpdate({
updateUrl: options.updateUrl,
hashName: options.hash,
});
@@ -90,19 +102,24 @@ export async function downloadUpdate(options) {
export function switchVersion(hash) {
assertRelease();
HotUpdate.reloadUpdate({ hashName: hash });
Pushy.reloadUpdate({ hashName: hash });
}
export function switchVersionLater(hash) {
assertRelease();
HotUpdate.setNeedUpdate({ hashName: hash });
Pushy.setNeedUpdate({ hashName: hash });
}
export function markSuccess() {
assertRelease();
HotUpdate.markSuccess();
Pushy.markSuccess();
}
NativeAppEventEmitter.addListener('RCTHotUpdateDownloadProgress', params => {});
// function report(action) {
// // ${project}.${host}/logstores/${logstore}/track?APIVersion=0.6.0&key1=val1
// fetch(`${logUrl}&action=${action}`);
// }
NativeAppEventEmitter.addListener('RCTHotUpdateUnzipProgress', params => {});
NativeAppEventEmitter.addListener('RCTPushyDownloadProgress', params => {});
NativeAppEventEmitter.addListener('RCTPushyUnzipProgress', params => {});

View File

@@ -1,14 +0,0 @@
{
"plugins": [
"syntax-object-rest-spread",
"syntax-async-functions",
"transform-es2015-arrow-functions",
"transform-async-to-generator",
"transform-es2015-modules-commonjs",
"transform-es2015-destructuring",
"transform-es2015-spread",
"transform-object-rest-spread",
"transform-es2015-parameters",
"transform-strict-mode"
]
}

View File

@@ -1,162 +0,0 @@
{
"useCommand": true,
"defaultCommand": "help",
"commands": {
"help": {
},
"login":{
},
"logout": {
},
"me": {
},
"createApp": {
"options": {
"name": {
"hasValue": true
},
"platform": {
"hasValue": true
}
}
},
"apps": {
"options": {
"platform": {
"hasValue": true
}
}
},
"deleteApp": {
},
"selectApp": {
"options": {
"platform": {
"hasValue": true
}
}
},
"uploadIpa": {
},
"uploadApk": {
},
"packages": {
"options": {
"platform": {
"hasValue": true
}
}
},
"publish": {
"options": {
"platform": {
"hasValue": true
},
"name": {
"hasValue": true
},
"description": {
"hasValue": true
},
"metaInfo": {
"hasValue": true
}
}
},
"versions": {
"options": {
"platform": {
"hasValue": true
}
}
},
"update": {
"options": {
"platform": {
"hasValue": true
},
"versionId": {
"hasValue": true
},
"packageId": {
"hasValue": true
}
}
},
"build": {
"description": "Bundle javascript and copy assets."
},
"bundle": {
"description": "Bundle javascript code only.",
"options": {
"dev": {
"default": "false",
"hasValue": true
},
"platform": {
"hasValue": true
},
"bundleName":{
"default": "index.bundlejs",
"hasValue": true
},
"entryFile": {
"default": "index.js",
"hasValue": true
},
"intermediaDir": {
"default": "build/intermedia/${platform}",
"hasValue": true
},
"output": {
"default": "build/output/${platform}.${time}.ppk",
"hasValue": true
},
"verbose": {
}
}
},
"release": {
"description": "Push builded file to server."
},
"diff": {
"description": "Create diff patch",
"options": {
"output": {
"default": "build/output/diff",
"hasValue": true
}
}
},
"diffFromApk": {
"description": "Create diff patch from a Android package(.apk)",
"options": {
"output": {
"default": "build/output/diff-${time}.apk-patch",
"hasValue": true
}
}
},
"diffFromIpa": {
"description": "Create diff patch from a iOS package(.ipa)",
"options": {
"output": {
"default": "build/output/diff-${time}.ipa-patch",
"hasValue": true
}
}
}
},
"globalOptions":{
"no-interactive": {
"default": false
}
}
}

View File

@@ -1,4 +0,0 @@
/**
* Created by tdzl2003 on 2/22/16.
*/
module.exports = require('./lib');

View File

@@ -1,132 +0,0 @@
/**
* Created by tdzl2003 on 2/13/16.
*/
const fetch = require('isomorphic-fetch');
let host = process.env.PUSHY_REGISTRY || 'https://update.reactnative.cn/api';
const fs = require('fs-extra');
import request from 'request';
import ProgressBar from 'progress';
let session = undefined;
let savedSession = undefined;
exports.loadSession = async function() {
if (fs.existsSync('.update')) {
try {
exports.replaceSession(JSON.parse(fs.readFileSync('.update', 'utf8')));
savedSession = session;
} catch (e) {
console.error('Failed to parse file `.update`. Try to remove it manually.');
throw e;
}
}
};
exports.getSession = function() {
return session;
};
exports.replaceSession = function(newSession) {
session = newSession;
};
exports.saveSession = function() {
// Only save on change.
if (session !== savedSession) {
const current = session;
const data = JSON.stringify(current, null, 4);
fs.writeFileSync('.update', data, 'utf8');
savedSession = current;
}
};
exports.closeSession = function() {
if (fs.existsSync('.update')) {
fs.unlinkSync('.update');
savedSession = undefined;
}
session = undefined;
host = process.env.PUSHY_REGISTRY || 'https://update.reactnative.cn';
};
async function query(url, options) {
const resp = await fetch(url, options);
const json = await resp.json();
if (resp.status !== 200) {
throw Object.assign(new Error(json.message || json.error), { status: resp.status });
}
return json;
}
function queryWithoutBody(method) {
return function(api) {
return query(host + api, {
method,
headers: {
'X-AccessToken': session ? session.token : '',
},
});
};
}
function queryWithBody(method) {
return function(api, body) {
return query(host + api, {
method,
headers: {
'Content-Type': 'application/json',
'X-AccessToken': session ? session.token : '',
},
body: JSON.stringify(body),
});
};
}
exports.get = queryWithoutBody('GET');
exports.post = queryWithBody('POST');
exports.put = queryWithBody('PUT');
exports.doDelete = queryWithBody('DELETE');
async function uploadFile(fn) {
const { url, fieldName, formData } = await exports.post('/upload', {});
let realUrl = url;
if (!/^https?\:\/\//.test(url)) {
realUrl = host + url;
}
const fileSize = fs.statSync(fn).size;
const bar = new ProgressBar(' Uploading [:bar] :percent :etas', {
complete: '=',
incomplete: ' ',
total: fileSize,
});
const info = await new Promise((resolve, reject) => {
formData.file = fs.createReadStream(fn);
formData.file.on('data', function(data) {
bar.tick(data.length);
});
request.post(
realUrl,
{
formData,
},
(err, resp, body) => {
if (err) {
return reject(err);
}
if (resp.statusCode > 299) {
return reject(Object.assign(new Error(body), { status: resp.statusCode }));
}
resolve(JSON.parse(body));
},
);
});
return info;
}
exports.uploadFile = uploadFile;

View File

@@ -1,110 +0,0 @@
/**
* Created by tdzl2003 on 2/13/16.
*/
import {question} from './utils';
import * as fs from 'fs-extra';
const {
post,
get,
doDelete,
} = require('./api');
const validPlatforms = {
ios: 1,
android: 1,
};
export function checkPlatform(platform) {
if (!validPlatforms[platform]) {
throw new Error(`Invalid platform '${platform}'`);
}
return platform
}
export function getSelectedApp(platform) {
checkPlatform(platform);
if (!fs.existsSync('update.json')){
throw new Error(`App not selected. run 'pushy selectApp --platform ${platform}' first!`);
}
const updateInfo = JSON.parse(fs.readFileSync('update.json', 'utf8'));
if (!updateInfo[platform]) {
throw new Error(`App not selected. run 'pushy selectApp --platform ${platform}' first!`);
}
return updateInfo[platform];
}
export async function listApp(platform){
const {data} = await get('/app/list');
const list = platform?data.filter(v=>v.platform===platform):data;
for (const app of list) {
console.log(`${app.id}) ${app.name}(${app.platform})`);
}
if (platform) {
console.log(`\nTotal ${list.length} ${platform} apps`);
} else {
console.log(`\nTotal ${list.length} apps`);
}
return list;
}
export async function chooseApp(platform) {
const list = await listApp(platform);
while (true) {
const id = await question('Enter appId:');
const app = list.find(v=>v.id === (id|0));
if (app) {
return app;
}
}
}
export const commands = {
createApp: async function ({options}) {
const name = options.name || await question('App Name:');
const {downloadUrl} = options;
const platform = checkPlatform(options.platform || await question('Platform(ios/android):'));
const {id} = await post('/app/create', {name, platform});
console.log(`Created app ${id}`);
await this.selectApp({
args: [id],
options: {platform, downloadUrl},
});
},
deleteApp: async function ({args, options}) {
const {platform} = options;
const id = args[0] || chooseApp(platform);
if (!id) {
console.log('Canceled');
}
await doDelete(`/app/${id}`);
console.log('Ok.');
},
apps: async function ({options}){
const {platform} = options;
listApp(platform);
},
selectApp: async function({args, options}) {
const platform = checkPlatform(options.platform || await question('Platform(ios/android):'));
const id = args[0] || (await chooseApp(platform)).id;
let updateInfo = {};
if (fs.existsSync('update.json')) {
try {
updateInfo = JSON.parse(fs.readFileSync('update.json', 'utf8'));
} catch (e) {
console.error('Failed to parse file `update.json`. Try to remove it manually.');
throw e;
}
}
const {appKey} = await get(`/app/${id}`);
updateInfo[platform] = {
appId: id,
appKey,
};
fs.writeFileSync('update.json', JSON.stringify(updateInfo, null, 4), 'utf8');
},
}

View File

@@ -1,506 +0,0 @@
/**
* Created by tdzl2003 on 2/22/16.
*/
const path = require('path');
import { getRNVersion, translateOptions } from './utils';
import * as fs from 'fs-extra';
import { ZipFile } from 'yazl';
import { open as openZipFile } from 'yauzl';
import { question } from './utils';
import { checkPlatform } from './app';
const { spawn, spawnSync, execSync } = require('child_process');
const g2js = require('gradle-to-js/lib/parser');
const os = require('os');
var diff;
try {
var bsdiff = require('node-bsdiff');
diff = typeof bsdiff != 'function' ? bsdiff.diff : bsdiff;
} catch (e) {
diff = function() {
console.warn(
'This function needs "node-bsdiff". Please run "npm i node-bsdiff" from your project directory first!',
);
throw new Error('This function needs module "node-bsdiff". Please install it first.');
};
}
function exec(command) {
const commandResult = spawnSync(command, {
shell: true,
stdio: 'inherit',
});
if (commandResult.error) {
throw commandResult.error;
}
}
async function runReactNativeBundleCommand(
bundleName,
development,
entryFile,
outputFolder,
platform,
sourcemapOutput,
config,
) {
let reactNativeBundleArgs = [];
let envArgs = process.env.PUSHY_ENV_ARGS;
if (envArgs) {
Array.prototype.push.apply(reactNativeBundleArgs, envArgs.trim().split(/\s+/));
}
fs.emptyDirSync(outputFolder);
Array.prototype.push.apply(reactNativeBundleArgs, [
path.join("node_modules", "react-native", "local-cli", "cli.js"),
"bundle",
'--assets-dest',
outputFolder,
'--bundle-output',
path.join(outputFolder, bundleName),
'--dev',
development,
'--entry-file',
entryFile,
'--platform',
platform,
]);
if (sourcemapOutput) {
reactNativeBundleArgs.push('--sourcemap-output', sourcemapOutput);
}
if (config) {
reactNativeBundleArgs.push('--config', config);
}
const reactNativeBundleProcess = spawn('node', reactNativeBundleArgs);
console.log(`Running bundle command: node ${reactNativeBundleArgs.join(' ')}`);
return new Promise((resolve, reject) => {
reactNativeBundleProcess.stdout.on('data', data => {
console.log(data.toString().trim());
});
reactNativeBundleProcess.stderr.on('data', data => {
console.error(data.toString().trim());
});
reactNativeBundleProcess.on('close', async exitCode => {
if (exitCode) {
reject(new Error(`"react-native bundle" command exited with code ${exitCode}.`));
} else {
if (platform === 'android') {
await compileHermesByteCode(bundleName, outputFolder);
}
resolve(null);
}
});
});
}
function getHermesOSBin() {
if (os.platform() === 'win32') return 'win64-bin';
if (os.platform() === 'darwin') return 'osx-bin';
if (os.platform() === 'linux') return 'linux64-bin';
}
async function compileHermesByteCode(bundleName, outputFolder) {
let enableHermes = false;
try {
const gradleConfig = await g2js.parseFile('android/app/build.gradle');
const projectConfig = gradleConfig['project.ext.react'];
for (const packagerConfig of projectConfig) {
if (packagerConfig.includes('enableHermes') && packagerConfig.includes('true')) {
enableHermes = true;
break;
}
}
} catch (e) {}
if (enableHermes) {
console.log(`Hermes enabled, now compiling to hermes bytecode:\n`);
const hermesPath = fs.existsSync('node_modules/hermes-engine')
? 'node_modules/hermes-engine'
: 'node_modules/hermesvm';
execSync(
`${hermesPath}/${getHermesOSBin()}/hermes -emit-binary -out ${outputFolder}/${bundleName} ${outputFolder}/${bundleName} -O`,
);
}
}
async function pack(dir, output) {
console.log('Packing');
fs.ensureDirSync(path.dirname(output));
await new Promise((resolve, reject) => {
var zipfile = new ZipFile();
function addDirectory(root, rel) {
if (rel) {
zipfile.addEmptyDirectory(rel);
}
const childs = fs.readdirSync(root);
for (const name of childs) {
if (name === '.' || name === '..') {
continue;
}
const fullPath = path.join(root, name);
const stat = fs.statSync(fullPath);
if (stat.isFile()) {
//console.log('adding: ' + rel+name);
zipfile.addFile(fullPath, rel + name);
} else if (stat.isDirectory()) {
//console.log('adding: ' + rel+name+'/');
addDirectory(fullPath, rel + name + '/');
}
}
}
addDirectory(dir, '');
zipfile.outputStream.on('error', err => reject(err));
zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function() {
resolve();
});
zipfile.end();
});
console.log('Bundled saved to: ' + output);
}
function readEntire(entry, zipFile) {
const buffers = [];
return new Promise((resolve, reject) => {
zipFile.openReadStream(entry, (err, stream) => {
stream.pipe({
write(chunk) {
buffers.push(chunk);
},
end() {
resolve(Buffer.concat(buffers));
},
prependListener() {},
on() {},
once() {},
emit() {},
});
});
});
}
function basename(fn) {
const m = /^(.+\/)[^\/]+\/?$/.exec(fn);
return m && m[1];
}
async function diffFromPPK(origin, next, output) {
fs.ensureDirSync(path.dirname(output));
const originEntries = {};
const originMap = {};
let originSource;
await enumZipEntries(origin, (entry, zipFile) => {
originEntries[entry.fileName] = entry;
if (!/\/$/.test(entry.fileName)) {
// isFile
originMap[entry.crc32] = entry.fileName;
if (entry.fileName === 'index.bundlejs') {
// This is source.
return readEntire(entry, zipFile).then(v => (originSource = v));
}
}
});
originSource = originSource || new Buffer(0);
const copies = {};
var zipfile = new ZipFile();
const writePromise = new Promise((resolve, reject) => {
zipfile.outputStream.on('error', err => {
throw err;
});
zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function() {
resolve();
});
});
const addedEntry = {};
function addEntry(fn) {
//console.log(fn);
if (!fn || addedEntry[fn]) {
return;
}
const base = basename(fn);
if (base) {
addEntry(base);
}
zipfile.addEmptyDirectory(fn);
}
const newEntries = {};
await enumZipEntries(next, (entry, nextZipfile) => {
newEntries[entry.fileName] = entry;
if (/\/$/.test(entry.fileName)) {
// Directory
if (!originEntries[entry.fileName]) {
addEntry(entry.fileName);
}
} else if (entry.fileName === 'index.bundlejs') {
//console.log('Found bundle');
return readEntire(entry, nextZipfile).then(newSource => {
//console.log('Begin diff');
zipfile.addBuffer(diff(originSource, newSource), 'index.bundlejs.patch');
//console.log('End diff');
});
} else {
// If same file.
const originEntry = originEntries[entry.fileName];
if (originEntry && originEntry.crc32 === entry.crc32) {
// ignore
return;
}
// If moved from other place
if (originMap[entry.crc32]) {
const base = basename(entry.fileName);
if (!originEntries[base]) {
addEntry(base);
}
copies[entry.fileName] = originMap[entry.crc32];
return;
}
// New file.
addEntry(basename(entry.fileName));
return new Promise((resolve, reject) => {
nextZipfile.openReadStream(entry, function(err, readStream) {
if (err) {
return reject(err);
}
zipfile.addReadStream(readStream, entry.fileName);
readStream.on('end', () => {
//console.log('add finished');
resolve();
});
});
});
}
});
const deletes = {};
for (var k in originEntries) {
if (!newEntries[k]) {
console.log('Delete ' + k);
deletes[k] = 1;
}
}
//console.log({copies, deletes});
zipfile.addBuffer(new Buffer(JSON.stringify({ copies, deletes })), '__diff.json');
zipfile.end();
await writePromise;
}
async function diffFromPackage(origin, next, output, originBundleName, transformPackagePath = v => v) {
fs.ensureDirSync(path.dirname(output));
const originEntries = {};
const originMap = {};
let originSource;
await enumZipEntries(origin, (entry, zipFile) => {
if (!/\/$/.test(entry.fileName)) {
const fn = transformPackagePath(entry.fileName);
if (!fn) {
return;
}
//console.log(fn);
// isFile
originEntries[fn] = entry.crc32;
originMap[entry.crc32] = fn;
if (fn === originBundleName) {
// This is source.
return readEntire(entry, zipFile).then(v => (originSource = v));
}
}
});
originSource = originSource || new Buffer(0);
const copies = {};
var zipfile = new ZipFile();
const writePromise = new Promise((resolve, reject) => {
zipfile.outputStream.on('error', err => {
throw err;
});
zipfile.outputStream.pipe(fs.createWriteStream(output)).on('close', function() {
resolve();
});
});
await enumZipEntries(next, (entry, nextZipfile) => {
if (/\/$/.test(entry.fileName)) {
// Directory
zipfile.addEmptyDirectory(entry.fileName);
} else if (entry.fileName === 'index.bundlejs') {
//console.log('Found bundle');
return readEntire(entry, nextZipfile).then(newSource => {
//console.log('Begin diff');
zipfile.addBuffer(diff(originSource, newSource), 'index.bundlejs.patch');
//console.log('End diff');
});
} else {
// If same file.
if (originEntries[entry.fileName] === entry.crc32) {
copies[entry.fileName] = '';
return;
}
// If moved from other place
if (originMap[entry.crc32]) {
copies[entry.fileName] = originMap[entry.crc32];
return;
}
return new Promise((resolve, reject) => {
nextZipfile.openReadStream(entry, function(err, readStream) {
if (err) {
return reject(err);
}
zipfile.addReadStream(readStream, entry.fileName);
readStream.on('end', () => {
//console.log('add finished');
resolve();
});
});
});
}
});
zipfile.addBuffer(new Buffer(JSON.stringify({ copies })), '__diff.json');
zipfile.end();
await writePromise;
}
function enumZipEntries(zipFn, callback) {
return new Promise((resolve, reject) => {
openZipFile(zipFn, { lazyEntries: true }, (err, zipfile) => {
if (err) {
return reject(err);
}
zipfile.on('end', resolve);
zipfile.on('error', reject);
zipfile.on('entry', entry => {
const result = callback(entry, zipfile);
if (result && typeof result.then === 'function') {
result.then(() => zipfile.readEntry());
} else {
zipfile.readEntry();
}
});
zipfile.readEntry();
});
});
}
export const commands = {
bundle: async function({ options }) {
const platform = checkPlatform(options.platform || (await question('Platform(ios/android):')));
let { bundleName, entryFile, intermediaDir, output, dev, verbose } = translateOptions({
...options,
platform,
});
// const sourcemapOutput = path.join(intermediaDir, bundleName + ".map");
const realOutput = output.replace(/\$\{time\}/g, '' + Date.now());
if (!platform) {
throw new Error('Platform must be specified.');
}
const { version, major, minor } = getRNVersion();
console.log('Bundling with React Native version: ', version);
await runReactNativeBundleCommand(bundleName, dev, entryFile, intermediaDir, platform);
await pack(path.resolve(intermediaDir), realOutput);
const v = await question('Would you like to publish it?(Y/N)');
if (v.toLowerCase() === 'y') {
await this.publish({
args: [realOutput],
options: {
platform,
},
});
}
},
async diff({ args, options }) {
const [origin, next] = args;
const { output } = options;
const realOutput = output.replace(/\$\{time\}/g, '' + Date.now());
if (!origin || !next) {
console.error('pushy diff <origin> <next>');
process.exit(1);
}
await diffFromPPK(origin, next, realOutput, 'index.bundlejs');
console.log(`${realOutput} generated.`);
},
async diffFromApk({ args, options }) {
const [origin, next] = args;
const { output } = options;
const realOutput = output.replace(/\$\{time\}/g, '' + Date.now());
if (!origin || !next) {
console.error('pushy diffFromApk <origin> <next>');
process.exit(1);
}
await diffFromPackage(origin, next, realOutput, 'assets/index.android.bundle');
console.log(`${realOutput} generated.`);
},
async diffFromIpa({ args, options }) {
const [origin, next] = args;
const { output } = options;
const realOutput = output.replace(/\$\{time\}/g, '' + Date.now());
if (!origin || !next) {
console.error('pushy diffFromIpa <origin> <next>');
process.exit(1);
}
await diffFromPackage(origin, next, realOutput, 'main.jsbundle', v => {
const m = /^Payload\/[^/]+\/(.+)$/.exec(v);
return m && m[1];
});
console.log(`${realOutput} generated.`);
},
};

View File

@@ -1,39 +0,0 @@
/**
* Created by tdzl2003 on 2/13/16.
*/
const {loadSession} = require('./api');
function printUsage({args}) {
// const commandName = args[0];
// TODO: print usage of commandName, or print global usage.
console.log('Usage is under development now.')
console.log('Visit `https://github.com/reactnativecn/react-native-pushy` for early document.');
process.exit(1);
}
const commands = {
...require('./user').commands,
...require('./bundle').commands,
...require('./app').commands,
...require('./package').commands,
...require('./versions').commands,
help: printUsage,
};
exports.run = function () {
const argv = require('cli-arguments').parse(require('../cli.json'));
global.NO_INTERACTIVE = argv.options['no-interactive'];
loadSession()
.then(()=>commands[argv.command](argv))
.catch(err=>{
if (err.status === 401) {
console.log('Not loggined.\nRun `pushy login` at your project directory to login.');
return;
}
console.error(err.stack);
process.exit(-1);
});
};

View File

@@ -1,85 +0,0 @@
/**
* Created by tdzl2003 on 4/2/16.
*/
const { get, post, uploadFile } = require('./api');
import { question } from './utils';
import { checkPlatform, getSelectedApp } from './app';
import { getIPAVersion, getApkVersion } from './utils';
const Table = require('tty-table');
export async function listPackage(appId) {
const { data } = await get(`/app/${appId}/package/list?limit=1000`);
const header = [{ value: 'Package Id' }, { value: 'Version' }];
const rows = [];
for (const pkg of data) {
const { version } = pkg;
let versionInfo = '';
if (version) {
versionInfo = ` - ${version.id} ${version.hash.slice(0, 8)} ${version.name}`;
} else {
versionInfo = ' (newest)';
}
rows.push([pkg.id, `${pkg.name}(${pkg.status})${versionInfo}`]);
}
console.log(Table(header, rows).render());
console.log(`\nTotal ${data.length} package(s).`);
return data;
}
export async function choosePackage(appId) {
const list = await listPackage(appId);
while (true) {
const id = await question('Enter Package Id:');
const app = list.find(v => v.id === (id | 0));
if (app) {
return app;
}
}
}
export const commands = {
uploadIpa: async function({ args }) {
const fn = args[0];
if (!fn) {
throw new Error('Usage: pushy uploadIpa <ipaFile>');
}
const name = await getIPAVersion(fn);
const { appId } = await getSelectedApp('ios');
const { hash } = await uploadFile(fn);
const { id } = await post(`/app/${appId}/package/create`, {
name,
hash,
});
console.log(`Ipa uploaded: ${id}`);
},
uploadApk: async function({ args }) {
const fn = args[0];
if (!fn) {
throw new Error('Usage: pushy uploadApk <apkFile>');
}
const name = await getApkVersion(fn);
const { appId } = await getSelectedApp('android');
const { hash } = await uploadFile(fn);
const { id } = await post(`/app/${appId}/package/create`, {
name,
hash,
});
console.log(`Apk uploaded: ${id}`);
},
packages: async function({ options }) {
const platform = checkPlatform(options.platform || (await question('Platform(ios/android):')));
const { appId } = await getSelectedApp(platform);
await listPackage(appId);
},
};

View File

@@ -1,43 +0,0 @@
/**
* Created by tdzl2003 on 2/13/16.
*/
import {question} from './utils';
const {
post,
get,
replaceSession,
saveSession,
closeSession,
} = require('./api');
const crypto = require('crypto');
function md5(str) {
return crypto.createHash('md5').update(str).digest('hex');
}
exports.commands = {
login: async function ({args}){
const email = args[0] || await question('email:');
const pwd = args[1] || await question('password:', true);
const {token, info} = await post('/user/login', {
email,
pwd: md5(pwd),
});
replaceSession({token});
await saveSession();
console.log(`Welcome, ${info.name}.`);
},
logout: async function (){
await closeSession();
console.log('Logged out.');
},
me: async function (){
const me = await get('/user/me');
for (const k in me) {
if (k !== 'ok') {
console.log(`${k}: ${me[k]}`);
}
}
},
}

View File

@@ -1,67 +0,0 @@
/**
* Created by tdzl2003 on 2/13/16.
*/
import * as path from 'path';
import * as fs from 'fs-extra';
const ApkReader = require('adbkit-apkreader');
import ipaReader from './ipaReader';
var read = require('read');
export function question(query, password) {
if (NO_INTERACTIVE) {
return Promise.resolve('');
}
return new Promise((resolve, reject) =>
read(
{
prompt: query,
silent: password,
replace: password ? '*' : undefined,
},
(err, result) => (err ? reject(err) : resolve(result)),
),
);
}
export function translateOptions(options) {
const ret = {};
for (let key in options) {
const v = options[key];
if (typeof v === 'string') {
ret[key] = v.replace(/\$\{(\w+)\}/g, function(v, n) {
return options[n] || process.env[n] || v;
});
} else {
ret[key] = v;
}
}
return ret;
}
export function getRNVersion() {
const version = JSON.parse(fs.readFileSync(path.resolve('node_modules/react-native/package.json'))).version;
// We only care about major and minor version.
const match = /^(\d+)\.(\d+)\./.exec(version);
return {
version,
major: match[1] | 0,
minor: match[2] | 0,
};
}
export async function getApkVersion(fn) {
const reader = await ApkReader.open(fn);
const manifest = await reader.readManifest();
return manifest.versionName;
}
export function getIPAVersion(fn) {
return new Promise((resolve, reject) => {
ipaReader(fn, (err, data) => {
err ? reject(err) : resolve(data.metadata.CFBundleShortVersionString);
});
});
}

View File

@@ -1,58 +0,0 @@
// var async = require('async');
var plist = require('simple-plist');
var decompress = require('decompress-zip');
// var provisioning = require('provisioning');
// var entitlements = require('entitlements');
var rimraf = require('rimraf');
var tmp = require('temporary');
var glob = require('glob');
var output = new tmp.Dir();
module.exports = function(file, callback) {
var data = {};
var unzipper = new decompress(file);
unzipper.extract({
path: output.path
});
unzipper.on('error', cleanUp);
unzipper.on('extract', function() {
var path = glob.sync(output.path + '/Payload/*/')[0];
data.metadata = plist.readFileSync(path + 'Info.plist');
cleanUp();
/*
var tasks = [async.apply(provisioning, path + 'embedded.mobileprovision')];
// `entitlements` relies on a OS X only CLI tool called `codesign`
if (process.platform === 'darwin') {
tasks.push(async.apply(entitlements, path));
}
async.parallel(tasks, function(error, results) {
if (error) {
return cleanUp(error);
}
data.provisioning = results[0];
// Hard to serialize and it looks messy in output
delete data.provisioning.DeveloperCertificates;
// Will be undefined on non-OSX platforms
data.entitlements = results[1];
return cleanUp();
});
*/
});
function cleanUp(error) {
rimraf.sync(output.path);
return callback(error, data);
}
};

View File

@@ -1,111 +0,0 @@
/**
* Created by tdzl2003 on 4/2/16.
*/
const {
get,
post,
put,
uploadFile,
} = require('./api');
import { question } from './utils';
import { checkPlatform, getSelectedApp } from './app';
import { choosePackage } from './package';
async function showVersion(appId, offset) {
const { data, count } = await get(`/app/${appId}/version/list`);
console.log(`Offset ${offset}`);
for (const version of data) {
let packageInfo = version.packages.slice(0, 3).map(v=>v.name).join(', ');
const count = version.packages.length;
if (count > 3) {
packageInfo += `...and ${count-3} more`;
}
if (count === 0) {
packageInfo = `(no package)`;
} else {
packageInfo = `[${packageInfo}]`;
}
console.log(`${version.id}) ${version.hash.slice(0, 8)} ${version.name} ${packageInfo}`);
}
return data;
}
async function listVersions(appId) {
let offset = 0;
while (true) {
await showVersion(appId, offset);
const cmd = await question('page Up/page Down/Begin/Quit(U/D/B/Q)');
switch (cmd.toLowerCase()) {
case 'u': offset = Math.max(0, offset - 10); break;
case 'd': offset += 10; break;
case 'b': offset = 0; break;
case 'q': return;
}
}
}
async function chooseVersion(appId) {
let offset = 0;
while (true) {
const data = await showVersion(appId, offset);
const cmd = await question('Enter versionId or page Up/page Down/Begin(U/D/B)');
switch (cmd.toLowerCase()) {
case 'U': offset = Math.max(0, offset - 10); break;
case 'D': offset += 10; break;
case 'B': offset = 0; break;
default:
{
const v = data.find(v=>v.id === (cmd | 0));
if (v) {
return v;
}
}
}
}
}
export const commands = {
publish: async function({args, options}) {
const fn = args[0];
const {name, description, metaInfo } = options;
if (!fn) {
throw new Error('Usage: pushy publish <ppkFile> --platform ios|android');
}
const platform = checkPlatform(options.platform || await question('Platform(ios/android):'));
const { appId } = await getSelectedApp(platform);
const { hash } = await uploadFile(fn);
const { id } = await post(`/app/${appId}/version/create`, {
name: name || await question('Enter version name:') || '(未命名)',
hash,
description: description || await question('Enter description:'),
metaInfo: metaInfo || await question('Enter meta info:'),
});
console.log(`Version published: ${id}`);
const v = await question('Would you like to bind packages to this version?(Y/N)');
if (v.toLowerCase() === 'y') {
await this.update({args:[], options:{versionId: id, platform}});
}
},
versions: async function({options}) {
const platform = checkPlatform(options.platform || await question('Platform(ios/android):'));
const { appId } = await getSelectedApp(platform);
await listVersions(appId);
},
update: async function({args, options}) {
const platform = checkPlatform(options.platform || await question('Platform(ios/android):'));
const { appId } = await getSelectedApp(platform);
const versionId = options.versionId || (await chooseVersion(appId)).id;
const pkgId = options.packageId || (await choosePackage(appId)).id;
await put(`/app/${appId}/package/${pkgId}`, {
versionId,
});
console.log('Ok.');
}
};

View File

@@ -1,12 +1,11 @@
{
"name": "react-native-update",
"version": "5.3.2",
"version": "5.5.10",
"description": "react-native hot update",
"main": "lib/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build-lib": "$ANDROID_HOME/ndk-bundle/ndk-build NDK_PROJECT_PATH=android APP_BUILD_SCRIPT=android/jni/Android.mk NDK_LIBS_OUT=android/lib",
"prepare": "node_modules/.bin/babel local-cli/src --out-dir local-cli/lib"
"build-lib": "$ANDROID_HOME/ndk-bundle/ndk-build NDK_PROJECT_PATH=android APP_BUILD_SCRIPT=android/jni/Android.mk NDK_LIBS_OUT=android/lib"
},
"repository": {
"type": "git",
@@ -27,35 +26,5 @@
"react-native": ">=0.27.0"
},
"homepage": "https://github.com/reactnativecn/react-native-pushy#readme",
"dependencies": {
"adbkit-apkreader": "^3.1.2",
"cli-arguments": "^0.2.1",
"decompress-zip": "^0.3.1",
"fs-extra": "^8.1.0",
"glob": "^7.1.2",
"gradle-to-js": "^2.0.0",
"isomorphic-fetch": "^2.2.1",
"progress": "^1.1.8",
"read": "^1.0.7",
"request": "^2.69.0",
"simple-plist": "^0.3.0",
"temporary": "^0.0.8",
"tty-table": "^2.7.0",
"yauzl": "2.4.1",
"yazl": "2.3.0"
},
"devDependencies": {
"babel-cli": "^6.5.1",
"babel-eslint": "^4.1.6",
"babel-plugin-syntax-async-functions": "^6.5.0",
"babel-plugin-syntax-object-rest-spread": "^6.5.0",
"babel-plugin-transform-async-to-generator": "^6.3.13",
"babel-plugin-transform-es2015-arrow-functions": "^6.5.2",
"babel-plugin-transform-es2015-destructuring": "^6.3.15",
"babel-plugin-transform-es2015-modules-commonjs": "^6.3.16",
"babel-plugin-transform-es2015-parameters": "^6.5.0",
"babel-plugin-transform-es2015-spread": "^6.5.2",
"babel-plugin-transform-object-rest-spread": "^6.5.0",
"babel-plugin-transform-strict-mode": "^6.5.2"
}
"dependencies": {}
}

View File

@@ -1,6 +0,0 @@
{
"plugins": [
"transform-es2015-modules-commonjs",
"transform-strict-mode"
]
}

View File

@@ -1,7 +0,0 @@
/.idea
/src
/.babelrc
/.npmignore
/.eslintrc
/.nvmrc
/.travis.yml

View File

@@ -1,37 +0,0 @@
{
"name": "react-native-update-cli",
"version": "0.1.0",
"description": "Command tools for javaScript updater with `pushy` service for react native apps.",
"main": "index.js",
"bin": {
"pushy": "lib/cli.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"prepublish": "node_modules/.bin/babel src --out-dir lib"
},
"repository": {
"type": "git",
"url": "git+https://github.com/reactnativecn/react-native-pushy.git"
},
"keywords": [
"react-native",
"ios",
"android",
"update"
],
"author": "reactnativecn",
"license": "BSD-3-Clause",
"bugs": {
"url": "https://github.com/reactnativecn/react-native-pushy/issues"
},
"dependencies": {
"fs-promise": "^0.4.1"
},
"homepage": "https://github.com/reactnativecn/react-native-pushy/tree/master/react-native-pushy-cli",
"devDependencies": {
"babel-cli": "^6.5.1",
"babel-plugin-transform-es2015-modules-commonjs": "^6.5.2",
"babel-plugin-transform-strict-mode": "^6.5.2"
}
}

View File

@@ -1,53 +0,0 @@
#!/usr/bin/env node
/**
* Created by tdzl2003 on 2/13/16.
*/
import * as path from 'path';
import * as fs from 'fs-extra';
const CLI_MODULE_PATH = function() {
return path.resolve(
process.cwd(),
'node_modules',
'react-native-update',
'local-cli'
);
};
const PACKAGE_JSON_PATH = function() {
return path.resolve(
process.cwd(),
'node_modules',
'react-native-update',
'package.json'
);
};
checkForVersionCommand();
let cli;
const cliPath = CLI_MODULE_PATH();
if (fs.existsSync(cliPath)) {
cli = require(cliPath);
}
if (cli) {
cli.run();
} else {
console.error('Are you at home directory of a react-native project?');
console.error('`pushy install` is under development, please run `npm install react-native-update` to install pushy manually.');
process.exit(1);
}
function checkForVersionCommand() {
if (process.argv.indexOf('-v') >= 0 || process.argv[2] === 'version') {
console.log('react-native-update-cli: ' + require('../package.json').version);
try {
console.log('react-native-update: ' + require(PACKAGE_JSON_PATH()).version);
} catch (e) {
console.log('react-native-update: n/a - not inside a React Native project directory')
}
process.exit();
}
}

View File

@@ -11,24 +11,25 @@ Pod::Spec.new do |s|
s.authors = package['author']
s.homepage = package['homepage']
s.platform = :ios, "7.0"
s.cocoapods_version = '>= 1.6.0'
s.platform = :ios, "8.0"
s.source = { :git => 'https://github.com/reactnativecn/react-native-pushy.git', :tag => '#{s.version}' }
s.libraries = 'bz2', 'z'
s.vendored_libraries = 'RCTHotUpdate/libRCTHotUpdate.a'
s.vendored_libraries = 'RCTPushy/libRCTPushy.a'
s.pod_target_xcconfig = { 'USER_HEADER_SEARCH_PATHS' => '"$(SRCROOT)/../node_modules/react-native-update/ios"' }
s.resource = 'ios/pushy_build_time.txt'
s.script_phase = { :name => 'Generate build time', :script => '../../node_modules/react-native-update/scripts/generateiOSBuildTime.sh', :execution_position => :before_compile }
s.script_phase = { :name => 'Generate build time', :script => 'set -x;date +%s > ${PODS_ROOT}/../../node_modules/react-native-update/ios/pushy_build_time.txt', :execution_position => :before_compile }
s.dependency 'React'
s.dependency 'SSZipArchive'
s.subspec 'RCTHotUpdate' do |ss|
ss.source_files = 'ios/RCTHotUpdate/*.{h,m}'
ss.public_header_files = ['ios/RCTHotUpdate/RCTHotUpdate.h']
s.subspec 'RCTPushy' do |ss|
ss.source_files = 'ios/RCTPushy/*.{h,m}'
ss.public_header_files = ['ios/RCTPushy/RCTPushy.h']
end
s.subspec 'BSDiff' do |ss|
ss.source_files = 'ios/RCTHotUpdate/BSDiff/**/*.{h,m,c}'
ss.private_header_files = 'ios/RCTHotUpdate/BSDiff/**/*.h'
ss.source_files = 'ios/RCTPushy/BSDiff/**/*.{h,m,c}'
ss.private_header_files = 'ios/RCTPushy/BSDiff/**/*.h'
end
end

View File

@@ -1,5 +0,0 @@
#!/bin/bash
set -x
DEST="../../node_modules/react-native-update/ios"
date +%s > "$DEST/pushy_build_time.txt"

Some files were not shown because too many files have changed in this diff Show More