mirror of
https://gitcode.com/gh_mirrors/re/react-native-pushy.git
synced 2025-09-17 12:52:20 +08:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
427b05f3a4 | ||
![]() |
73c3dc538c | ||
![]() |
6abc960dac | ||
![]() |
98b89c422d | ||
![]() |
ac60284ae1 | ||
![]() |
6c1a4f148c | ||
![]() |
e0857d48ad | ||
![]() |
d9bbaf77aa | ||
![]() |
736699a922 | ||
![]() |
91f877dc6d |
@@ -12,7 +12,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "17.0.2",
|
||||
"react-native": "0.68.2",
|
||||
"react-native": "0.68.5",
|
||||
"react-native-update": "link:../.."
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@@ -5424,10 +5424,10 @@ progress@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
||||
promise@^8.0.3:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e"
|
||||
integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==
|
||||
promise@^8.2.0:
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/promise/-/promise-8.3.0.tgz#8cb333d1edeb61ef23869fbb8a4ea0279ab60e0a"
|
||||
integrity sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==
|
||||
dependencies:
|
||||
asap "~2.0.6"
|
||||
|
||||
@@ -5494,10 +5494,10 @@ react-is@^16.13.1:
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-native-codegen@^0.0.17:
|
||||
version "0.0.17"
|
||||
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.17.tgz#83fb814d94061cbd46667f510d2ddba35ffb50ac"
|
||||
integrity sha512-7GIEUmAemH9uWwB6iYXNNsPoPgH06pxzGRmdBzK98TgFBdYJZ7CBuZFPMe4jmHQTPOkQazKZ/w5O6/71JBixmw==
|
||||
react-native-codegen@^0.0.18:
|
||||
version "0.0.18"
|
||||
resolved "https://registry.yarnpkg.com/react-native-codegen/-/react-native-codegen-0.0.18.tgz#99d6623d65292e8ce3fdb1d133a358caaa2145e7"
|
||||
integrity sha512-XPI9aVsFy3dvgDZvyGWrFnknNiyb22kg5nHgxa0vjWTH9ENLBgVRZt9A64xHZ8BYihH+gl0p/1JNOCIEUzRPBg==
|
||||
dependencies:
|
||||
"@babel/parser" "^7.14.0"
|
||||
flow-parser "^0.121.0"
|
||||
@@ -5513,10 +5513,10 @@ react-native-gradle-plugin@^0.0.6:
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
react-native@0.68.2:
|
||||
version "0.68.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.2.tgz#07547cd31bb9335a7fa4135cfbdc24e067142585"
|
||||
integrity sha512-qNMz+mdIirCEmlrhapAtAG+SWVx6MAiSfCbFNhfHqiqu1xw1OKXdzIrjaBEPihRC2pcORCoCHduHGQe/Pz9Yuw==
|
||||
react-native@0.68.5:
|
||||
version "0.68.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.68.5.tgz#8ba7389e00b757c59b6ea23bf38303d52367d155"
|
||||
integrity sha512-t3kiQ/gumFV+0r/NRSIGtYxanjY4da0utFqHgkMcRPJVwXFWC0Fr8YiOeRGYO1dp8EfrSsOjtfWic/inqVYlbQ==
|
||||
dependencies:
|
||||
"@jest/create-cache-key-function" "^27.0.1"
|
||||
"@react-native-community/cli" "^7.0.3"
|
||||
@@ -5538,9 +5538,9 @@ react-native@0.68.2:
|
||||
metro-source-map "0.67.0"
|
||||
nullthrows "^1.1.1"
|
||||
pretty-format "^26.5.2"
|
||||
promise "^8.0.3"
|
||||
promise "^8.2.0"
|
||||
react-devtools-core "^4.23.0"
|
||||
react-native-codegen "^0.0.17"
|
||||
react-native-codegen "^0.0.18"
|
||||
react-native-gradle-plugin "^0.0.6"
|
||||
react-refresh "^0.4.0"
|
||||
react-shallow-renderer "16.14.1"
|
||||
|
12
README.md
12
README.md
@@ -9,11 +9,13 @@
|
||||
### 优势
|
||||
|
||||
1. 基于阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。
|
||||
2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在 1-10KB 之间,避免数百 KB 的流量消耗。
|
||||
3. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
||||
4. 命令行工具&网页双端管理,版本发布过程简单便捷,完全可以集成 CI。
|
||||
5. 支持崩溃回滚,安全可靠。
|
||||
6. meta 信息及开放 API,提供更高扩展性。
|
||||
2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在几十 KB 级别(其他全量热更新服务所需流量通常在几十 MB 级别)。
|
||||
3. 始终跟进 RN 最新正式版本,第一时间提供支持。支持 hermes 字节码格式。(暂不支持新架构,会待其相对稳定后跟进)
|
||||
4. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
||||
5. 命令行工具 & 网页双端管理,版本发布过程简单便捷,完全可以集成 CI。
|
||||
6. 支持崩溃回滚,安全可靠。
|
||||
7. meta 信息及开放 API,提供更高扩展性。
|
||||
8. 提供付费的专人技术支持。
|
||||
|
||||
### 本地开发
|
||||
|
||||
|
1
android/proguard.pro
vendored
1
android/proguard.pro
vendored
@@ -1,2 +1,3 @@
|
||||
-keepnames class cn.reactnative.modules.update.DownloadTask { *; }
|
||||
-keepnames class cn.reactnative.modules.update.UpdateModule { *; }
|
||||
-keepnames class com.facebook.react.ReactInstanceManager { *; }
|
9
lib/index.d.ts
vendored
9
lib/index.d.ts
vendored
@@ -84,4 +84,11 @@ interface ProgressData {
|
||||
total: number;
|
||||
}
|
||||
|
||||
export function simpleUpdate(wrappedComponent: any): any;
|
||||
interface SimpleUpdateOptions {
|
||||
appKey: string;
|
||||
}
|
||||
|
||||
export function simpleUpdate(
|
||||
wrappedComponent: any,
|
||||
options: SimpleUpdateOptions,
|
||||
): any;
|
||||
|
17
lib/index.web.js
Normal file
17
lib/index.web.js
Normal file
@@ -0,0 +1,17 @@
|
||||
export const downloadRootDir = '';
|
||||
export const packageVersion = '';
|
||||
export const currentVersion = '';
|
||||
export const isFirstTime = false;
|
||||
export const isRolledBack = false;
|
||||
|
||||
const noop = () => {};
|
||||
|
||||
export const checkUpdate = noop;
|
||||
export const downloadUpdate = noop;
|
||||
export const switchVersion = noop;
|
||||
export const switchVersionLater = noop;
|
||||
export const markSuccess = noop;
|
||||
export const downloadAndInstallApk = noop;
|
||||
export const setCustomEndpoints = noop;
|
||||
export const getCurrentVersionInfo = noop;
|
||||
export const simpleUpdate = noop;
|
109
lib/main.js
109
lib/main.js
@@ -58,34 +58,42 @@ if (!uuid) {
|
||||
Pushy.setUuid(uuid);
|
||||
}
|
||||
|
||||
function logger(text) {
|
||||
console.log(`Pushy: ${text}`);
|
||||
function logger(...args) {
|
||||
console.log('Pushy: ', ...args);
|
||||
}
|
||||
|
||||
function report(hash, type) {
|
||||
logger(type);
|
||||
fetch(getReportUrl(), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
hash,
|
||||
type,
|
||||
const noop = () => {};
|
||||
let reporter = noop;
|
||||
|
||||
export function onEvents(customReporter) {
|
||||
reporter = customReporter;
|
||||
if (isRolledBack) {
|
||||
report({
|
||||
type: 'rollback',
|
||||
data: {
|
||||
rolledBackVersion,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function report({ type, message = '', data = {} }) {
|
||||
logger(type + ' ' + message);
|
||||
reporter({
|
||||
type,
|
||||
data: {
|
||||
currentVersion,
|
||||
cInfo,
|
||||
packageVersion,
|
||||
buildTime,
|
||||
}),
|
||||
}).catch((_e) => {});
|
||||
message,
|
||||
...data,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
logger('uuid: ' + uuid);
|
||||
|
||||
if (isRolledBack) {
|
||||
report(rolledBackVersion, 'rollback');
|
||||
}
|
||||
|
||||
export const cInfo = {
|
||||
pushy: require('../package.json').version,
|
||||
rn: RNVersion,
|
||||
@@ -99,28 +107,30 @@ function assertRelease() {
|
||||
}
|
||||
}
|
||||
|
||||
let checkingThrottling = false;
|
||||
let lastChecking;
|
||||
const empty = {};
|
||||
let lastResult;
|
||||
export async function checkUpdate(APPKEY, isRetry) {
|
||||
assertRelease();
|
||||
if (checkingThrottling) {
|
||||
logger('repeated checking, ignored');
|
||||
return;
|
||||
}
|
||||
checkingThrottling = true;
|
||||
setTimeout(() => {
|
||||
checkingThrottling = false;
|
||||
}, 3000);
|
||||
if (blockUpdate && blockUpdate.until > Date.now() / 1000) {
|
||||
throw new Error(
|
||||
`热更新已暂停,原因:${blockUpdate.reason}。请在"${new Date(
|
||||
blockUpdate.until * 1000,
|
||||
).toLocaleString()}"之后重试。`,
|
||||
);
|
||||
}
|
||||
if (typeof APPKEY !== 'string') {
|
||||
throw new Error('未检查到合法的APPKEY,请查看update.json文件是否正确生成');
|
||||
}
|
||||
logger('checking update');
|
||||
assertRelease();
|
||||
const now = Date.now();
|
||||
if (lastResult && lastChecking && now - lastChecking < 1000 * 60) {
|
||||
// logger('repeated checking, ignored');
|
||||
return lastResult;
|
||||
}
|
||||
lastChecking = now;
|
||||
if (blockUpdate && blockUpdate.until > Date.now() / 1000) {
|
||||
report({
|
||||
type: 'errorChecking',
|
||||
message: `热更新已暂停,原因:${blockUpdate.reason}。请在"${new Date(
|
||||
blockUpdate.until * 1000,
|
||||
).toLocaleString()}"之后重试。`,
|
||||
});
|
||||
return lastResult || empty;
|
||||
}
|
||||
report({ type: 'checking' });
|
||||
let resp;
|
||||
try {
|
||||
resp = await fetch(getCheckUrl(APPKEY), {
|
||||
@@ -138,16 +148,25 @@ export async function checkUpdate(APPKEY, isRetry) {
|
||||
});
|
||||
} catch (e) {
|
||||
if (isRetry) {
|
||||
throw new Error('无法连接更新服务器,请检查网络连接后重试');
|
||||
report({
|
||||
type: 'errorChecking',
|
||||
message: '无法连接更新服务器,请检查网络连接后重试',
|
||||
});
|
||||
return lastResult || empty;
|
||||
}
|
||||
await tryBackupEndpoints(APPKEY);
|
||||
await tryBackupEndpoints();
|
||||
return checkUpdate(APPKEY, true);
|
||||
}
|
||||
const result = await resp.json();
|
||||
lastResult = result;
|
||||
|
||||
checkOperation(result.op);
|
||||
|
||||
if (resp.status !== 200) {
|
||||
throw new Error(result.message);
|
||||
report({
|
||||
type: 'errorChecking',
|
||||
message: result.message,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -206,6 +225,7 @@ export async function downloadUpdate(options, eventListeners) {
|
||||
}
|
||||
}
|
||||
let succeeded = false;
|
||||
report({ type: 'downloading' });
|
||||
if (options.diffUrl) {
|
||||
logger('downloading diff');
|
||||
try {
|
||||
@@ -245,8 +265,7 @@ export async function downloadUpdate(options, eventListeners) {
|
||||
}
|
||||
progressHandler && progressHandler.remove();
|
||||
if (!succeeded) {
|
||||
report(options.hash, 'error');
|
||||
throw new Error('all update attempts failed');
|
||||
return report({ type: 'errorUpdate', data: { newVersion: options.hash } });
|
||||
}
|
||||
setLocalHashInfo(options.hash, {
|
||||
name: options.name,
|
||||
@@ -305,10 +324,10 @@ export async function downloadAndInstallApk({ url, onDownloadProgress }) {
|
||||
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
||||
);
|
||||
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
|
||||
return;
|
||||
return report({ type: 'rejectStoragePermission' });
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(err);
|
||||
return report({ type: 'errorStoragePermission' });
|
||||
}
|
||||
}
|
||||
let hash = Date.now().toString();
|
||||
@@ -327,6 +346,8 @@ export async function downloadAndInstallApk({ url, onDownloadProgress }) {
|
||||
url,
|
||||
target: 'update.apk',
|
||||
hash,
|
||||
}).catch(() => {
|
||||
report({ type: 'errowDownloadAndInstallApk' });
|
||||
});
|
||||
progressHandler && progressHandler.remove();
|
||||
}
|
||||
|
@@ -12,10 +12,11 @@ import {
|
||||
downloadAndInstallApk,
|
||||
} from './main';
|
||||
|
||||
import _updateConfig from '../../../update.json';
|
||||
const { appKey } = _updateConfig[Platform.OS];
|
||||
|
||||
export function simpleUpdate(WrappedComponent) {
|
||||
export function simpleUpdate(WrappedComponent, options = {}) {
|
||||
const { appKey } = options;
|
||||
if (!appKey) {
|
||||
throw new Error('appKey is required for simpleUpdate()');
|
||||
}
|
||||
return __DEV__
|
||||
? WrappedComponent
|
||||
: class AppUpdate extends Component {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-update",
|
||||
"version": "7.4.3",
|
||||
"version": "8.2.0",
|
||||
"description": "react-native hot update",
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
|
Reference in New Issue
Block a user