react-native-update 10.42.6 → 10.42.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets +1 -1
- package/harmony/pushy/src/main/ets/PushyTurboModule.ts +16 -2
- package/harmony/pushy/src/main/ets/UpdateContext.ts +78 -6
- package/harmony/pushy.har +0 -0
- package/ios/ImportReact.h +2 -0
- package/package.json +1 -1
- package/react-native-update.podspec +1 -0
- package/scripts/prepublish.ts +13 -0
- package/src/core.ts +49 -41
|
@@ -12,7 +12,7 @@ export class PushyFileJSBundleProvider extends JSBundleProvider {
|
|
|
12
12
|
|
|
13
13
|
constructor(context: common.UIAbilityContext) {
|
|
14
14
|
super();
|
|
15
|
-
this.updateContext =
|
|
15
|
+
this.updateContext = UpdateContext.getInstance(context);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
getURL(): string {
|
|
@@ -38,7 +38,7 @@ export class PushyTurboModule extends UITurboModule {
|
|
|
38
38
|
super(ctx);
|
|
39
39
|
logger.debug(TAG, ',PushyTurboModule constructor');
|
|
40
40
|
this.mUiCtx = ctx.uiAbilityContext;
|
|
41
|
-
this.context =
|
|
41
|
+
this.context = UpdateContext.getInstance(this.mUiCtx);
|
|
42
42
|
EventHub.getInstance().setRNInstance(ctx.rnInstance);
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -78,6 +78,7 @@ export class PushyTurboModule extends UITurboModule {
|
|
|
78
78
|
|
|
79
79
|
getConstants(): Object {
|
|
80
80
|
logger.debug(TAG, ',call getConstants');
|
|
81
|
+
this.context.logStateSnapshot('getConstants:enter');
|
|
81
82
|
const packageVersion = this.context.getPackageVersion();
|
|
82
83
|
const buildTime = this.context.getBuildTime();
|
|
83
84
|
this.context.syncStateWithBinaryVersion(packageVersion, buildTime);
|
|
@@ -95,7 +96,7 @@ export class PushyTurboModule extends UITurboModule {
|
|
|
95
96
|
this.context.clearRollbackMark();
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
const result = {
|
|
99
100
|
downloadRootDir: `${this.mUiCtx.filesDir}/_update`,
|
|
100
101
|
currentVersionInfo,
|
|
101
102
|
packageVersion,
|
|
@@ -106,6 +107,19 @@ export class PushyTurboModule extends UITurboModule {
|
|
|
106
107
|
rolledBackVersion,
|
|
107
108
|
uuid,
|
|
108
109
|
};
|
|
110
|
+
const logResult = {
|
|
111
|
+
downloadRootDir: result.downloadRootDir,
|
|
112
|
+
currentVersionInfo: result.currentVersionInfo,
|
|
113
|
+
packageVersion: result.packageVersion,
|
|
114
|
+
currentVersion: result.currentVersion,
|
|
115
|
+
buildTime: result.buildTime,
|
|
116
|
+
isUsingBundleUrl: result.isUsingBundleUrl,
|
|
117
|
+
isFirstTime: result.isFirstTime,
|
|
118
|
+
rolledBackVersion: result.rolledBackVersion,
|
|
119
|
+
uuidSet: !!result.uuid,
|
|
120
|
+
};
|
|
121
|
+
logger.info(TAG, `,getConstants result: ${JSON.stringify(logResult)}`);
|
|
122
|
+
return result;
|
|
109
123
|
}
|
|
110
124
|
|
|
111
125
|
setLocalHashInfo(hash: string, info: string): boolean {
|
|
@@ -5,6 +5,7 @@ import common from '@ohos.app.ability.common';
|
|
|
5
5
|
import { DownloadTaskParams } from './DownloadTaskParams';
|
|
6
6
|
import { bundleManager } from '@kit.AbilityKit';
|
|
7
7
|
import { util } from '@kit.ArkTS';
|
|
8
|
+
import logger from './Logger';
|
|
8
9
|
import NativePatchCore, {
|
|
9
10
|
STATE_OP_CLEAR_FIRST_TIME,
|
|
10
11
|
STATE_OP_CLEAR_ROLLBACK_MARK,
|
|
@@ -26,10 +27,25 @@ export class UpdateContext {
|
|
|
26
27
|
private static DEBUG: boolean = false;
|
|
27
28
|
private static isUsingBundleUrl: boolean = false;
|
|
28
29
|
private static ignoreRollback: boolean = false;
|
|
30
|
+
private static cachedPackageVersion: string = '';
|
|
31
|
+
private static cachedBuildTime: string = '';
|
|
32
|
+
// 单例:确保 bundle provider 与 TurboModule 共用同一份 preferences 内存状态,
|
|
33
|
+
// 避免 RNOH RN 实例重建后两处 UpdateContext 各自持有 preferences 缓存导致读写分裂。
|
|
34
|
+
private static instance: UpdateContext | null = null;
|
|
35
|
+
private static instanceCounter: number = 0;
|
|
36
|
+
private readonly instanceId: string;
|
|
37
|
+
|
|
38
|
+
public static getInstance(context: common.UIAbilityContext): UpdateContext {
|
|
39
|
+
if (!UpdateContext.instance) {
|
|
40
|
+
UpdateContext.instance = new UpdateContext(context);
|
|
41
|
+
}
|
|
42
|
+
return UpdateContext.instance;
|
|
43
|
+
}
|
|
29
44
|
|
|
30
|
-
constructor(context: common.UIAbilityContext) {
|
|
45
|
+
private constructor(context: common.UIAbilityContext) {
|
|
31
46
|
this.context = context;
|
|
32
47
|
this.rootDir = context.filesDir + '/_update';
|
|
48
|
+
this.instanceId = `uc#${++UpdateContext.instanceCounter}`;
|
|
33
49
|
|
|
34
50
|
try {
|
|
35
51
|
if (!fileIo.accessSync(this.rootDir)) {
|
|
@@ -39,12 +55,36 @@ export class UpdateContext {
|
|
|
39
55
|
console.error('Failed to create root directory:', e);
|
|
40
56
|
}
|
|
41
57
|
this.initPreferences();
|
|
58
|
+
this.trace('ctor');
|
|
42
59
|
this.syncStateWithBinaryVersion(
|
|
43
60
|
this.getPackageVersion(),
|
|
44
61
|
this.getBuildTime(),
|
|
45
62
|
);
|
|
46
63
|
}
|
|
47
64
|
|
|
65
|
+
/**
|
|
66
|
+
* 诊断日志:打印本实例 id 与关键状态,用于定位 preferences 多实例 / 状态分裂问题。
|
|
67
|
+
* 通过 hilog 输出,prefix=pushy,可在 hilog 中按 "UpdateContext" 过滤。
|
|
68
|
+
*/
|
|
69
|
+
private trace(point: string): void {
|
|
70
|
+
const snap = this.getStateSnapshot();
|
|
71
|
+
logger.info(
|
|
72
|
+
'UpdateContext',
|
|
73
|
+
`trace id=${this.instanceId} ${point}` +
|
|
74
|
+
` pkg=${snap.packageVersion} bt=${snap.buildTime}` +
|
|
75
|
+
` cv=${snap.currentVersion} lv=${snap.lastVersion}` +
|
|
76
|
+
` ft=${snap.firstTime} fto=${snap.firstTimeOk}` +
|
|
77
|
+
` rb=${snap.rolledBackVersion}` +
|
|
78
|
+
` flm=${this.readString('firstLoadMarked')}` +
|
|
79
|
+
` uuidSet=${!!this.readString('uuid')}`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** 对外诊断入口,供 TurboModule 在 getConstants 等关键节点打印状态。 */
|
|
84
|
+
public logStateSnapshot(point: string): void {
|
|
85
|
+
this.trace(point);
|
|
86
|
+
}
|
|
87
|
+
|
|
48
88
|
private initPreferences() {
|
|
49
89
|
try {
|
|
50
90
|
this.preferences = preferences.getPreferencesSync(this.context, {
|
|
@@ -60,11 +100,15 @@ export class UpdateContext {
|
|
|
60
100
|
}
|
|
61
101
|
|
|
62
102
|
public getPackageVersion(): string {
|
|
103
|
+
if (UpdateContext.cachedPackageVersion) {
|
|
104
|
+
return UpdateContext.cachedPackageVersion;
|
|
105
|
+
}
|
|
63
106
|
try {
|
|
64
107
|
const bundleInfo = bundleManager.getBundleInfoForSelfSync(
|
|
65
108
|
this.getBundleFlags(),
|
|
66
109
|
);
|
|
67
|
-
|
|
110
|
+
UpdateContext.cachedPackageVersion = bundleInfo?.versionName || 'Unknown';
|
|
111
|
+
return UpdateContext.cachedPackageVersion;
|
|
68
112
|
} catch (error) {
|
|
69
113
|
console.error('Failed to get bundle info:', error);
|
|
70
114
|
return '';
|
|
@@ -72,6 +116,9 @@ export class UpdateContext {
|
|
|
72
116
|
}
|
|
73
117
|
|
|
74
118
|
public getBuildTime(): string {
|
|
119
|
+
if (UpdateContext.cachedBuildTime) {
|
|
120
|
+
return UpdateContext.cachedBuildTime;
|
|
121
|
+
}
|
|
75
122
|
try {
|
|
76
123
|
const content =
|
|
77
124
|
this.context.resourceManager.getRawFileContentSync('meta.json');
|
|
@@ -79,9 +126,12 @@ export class UpdateContext {
|
|
|
79
126
|
new util.TextDecoder().decodeToString(content),
|
|
80
127
|
) as Record<string, string | number | boolean | null | undefined>;
|
|
81
128
|
if (metaData.pushy_build_time) {
|
|
82
|
-
|
|
129
|
+
UpdateContext.cachedBuildTime = String(metaData.pushy_build_time);
|
|
130
|
+
return UpdateContext.cachedBuildTime;
|
|
83
131
|
}
|
|
84
|
-
} catch {
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error('Failed to read build time from raw file:', error);
|
|
134
|
+
}
|
|
85
135
|
return '';
|
|
86
136
|
}
|
|
87
137
|
|
|
@@ -244,6 +294,9 @@ export class UpdateContext {
|
|
|
244
294
|
packageVersion: string,
|
|
245
295
|
buildTime: string,
|
|
246
296
|
): void {
|
|
297
|
+
if (!packageVersion || !buildTime) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
247
300
|
const currentState = this.getStateSnapshot();
|
|
248
301
|
const nextState = NativePatchCore.syncStateWithBinaryVersion(
|
|
249
302
|
packageVersion,
|
|
@@ -254,9 +307,17 @@ export class UpdateContext {
|
|
|
254
307
|
return;
|
|
255
308
|
}
|
|
256
309
|
|
|
310
|
+
logger.info(
|
|
311
|
+
'UpdateContext',
|
|
312
|
+
`binary version changed, resetting update state id=${this.instanceId}`,
|
|
313
|
+
);
|
|
257
314
|
UpdateContext.ignoreRollback = false;
|
|
258
315
|
this.cleanUp();
|
|
259
|
-
|
|
316
|
+
// 仅重置状态机字段(currentVersion / lastVersion / firstTime / firstTimeOk /
|
|
317
|
+
// rolledBackVersion)。不再 clearExisting,避免连带清除 uuid / firstLoadMarked /
|
|
318
|
+
// hash_* 等与 binary 版本无关的 KV —— 它们在多实例场景下本就脆弱,连带清除会
|
|
319
|
+
// 让 getConstants() 永远读到空,从而 isFirstTime=false、markSuccess 永不执行。
|
|
320
|
+
this.persistState(nextState);
|
|
260
321
|
}
|
|
261
322
|
|
|
262
323
|
public setKv(key: string, value: string): void {
|
|
@@ -373,8 +434,10 @@ export class UpdateContext {
|
|
|
373
434
|
throw Error(`Bundle version ${hash} not found.`);
|
|
374
435
|
}
|
|
375
436
|
|
|
437
|
+
this.trace(`switchVersion:before ${hash}`);
|
|
376
438
|
this.runStateOperation(STATE_OP_SWITCH_VERSION, hash);
|
|
377
439
|
UpdateContext.ignoreRollback = false;
|
|
440
|
+
this.trace(`switchVersion:after ${hash}`);
|
|
378
441
|
} catch (e) {
|
|
379
442
|
console.error('Failed to switch version:', e);
|
|
380
443
|
throw e;
|
|
@@ -383,6 +446,7 @@ export class UpdateContext {
|
|
|
383
446
|
|
|
384
447
|
public consumeFirstLoadMarker(): boolean {
|
|
385
448
|
const marked = this.readString('firstLoadMarked') === 'true';
|
|
449
|
+
this.trace(`consumeFirstLoadMarker:marked=${marked}`);
|
|
386
450
|
if (marked) {
|
|
387
451
|
this.preferences.deleteSync('firstLoadMarked');
|
|
388
452
|
this.flushPreferences('clear first load marker');
|
|
@@ -392,6 +456,7 @@ export class UpdateContext {
|
|
|
392
456
|
|
|
393
457
|
public getBundleUrl() {
|
|
394
458
|
UpdateContext.isUsingBundleUrl = true;
|
|
459
|
+
this.trace('getBundleUrl:enter');
|
|
395
460
|
const launchState = NativePatchCore.runStateCore(
|
|
396
461
|
STATE_OP_RESOLVE_LAUNCH,
|
|
397
462
|
this.getStateSnapshot(),
|
|
@@ -407,6 +472,11 @@ export class UpdateContext {
|
|
|
407
472
|
if (launchState.consumedFirstTime) {
|
|
408
473
|
UpdateContext.ignoreRollback = true;
|
|
409
474
|
}
|
|
475
|
+
this.trace(
|
|
476
|
+
`getBundleUrl:load=${launchState.loadVersion}` +
|
|
477
|
+
` consumed=${launchState.consumedFirstTime}` +
|
|
478
|
+
` rollback=${launchState.didRollback}`,
|
|
479
|
+
);
|
|
410
480
|
|
|
411
481
|
let version = launchState.loadVersion || '';
|
|
412
482
|
while (version) {
|
|
@@ -427,7 +497,9 @@ export class UpdateContext {
|
|
|
427
497
|
}
|
|
428
498
|
|
|
429
499
|
public getCurrentVersion(): string {
|
|
430
|
-
|
|
500
|
+
const cv = this.getStateSnapshot().currentVersion || '';
|
|
501
|
+
this.trace(`getCurrentVersion:${cv}`);
|
|
502
|
+
return cv;
|
|
431
503
|
}
|
|
432
504
|
|
|
433
505
|
private rollBack(): string {
|
package/harmony/pushy.har
CHANGED
|
Binary file
|
package/package.json
CHANGED
package/scripts/prepublish.ts
CHANGED
|
@@ -8,6 +8,12 @@ function normalizeVersion(version: string): string {
|
|
|
8
8
|
return version.trim().replace(/^refs\/tags\//, '').replace(/^v/, '');
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
const SEMVER_REGEX = /^\d+\.\d+\.\d+(-.+)?$/;
|
|
12
|
+
|
|
13
|
+
function isValidSemver(version: string): boolean {
|
|
14
|
+
return SEMVER_REGEX.test(version);
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
function getVersionFromEnvironment(): string | null {
|
|
12
18
|
const candidates = [
|
|
13
19
|
process.env.RELEASE_VERSION,
|
|
@@ -134,6 +140,13 @@ async function main(): Promise<void> {
|
|
|
134
140
|
try {
|
|
135
141
|
if (isGitHubCI()) {
|
|
136
142
|
const version = await resolveVersion();
|
|
143
|
+
if (!isValidSemver(version)) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Resolved version "${version}" is not a valid semantic version. ` +
|
|
146
|
+
`Please ensure RELEASE_VERSION, GITHUB_REF, or GITHUB_REF_NAME is set correctly, ` +
|
|
147
|
+
`or that the git history is fully fetched.`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
137
150
|
console.log(`Using publish version ${version}`);
|
|
138
151
|
await modifyPackageJson({ version });
|
|
139
152
|
} else {
|
package/src/core.ts
CHANGED
|
@@ -1,58 +1,59 @@
|
|
|
1
1
|
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
|
2
|
-
import { emptyModule, error, log } from './utils';
|
|
3
2
|
import i18n from './i18n';
|
|
3
|
+
import { emptyModule, error, log } from './utils';
|
|
4
|
+
|
|
4
5
|
/* eslint-disable @react-native/no-deep-imports */
|
|
5
6
|
const {
|
|
6
|
-
|
|
7
|
+
version: v,
|
|
7
8
|
} = require('react-native/Libraries/Core/ReactNativeVersion');
|
|
8
9
|
const RNVersion = `${v.major}.${v.minor}.${v.patch}`;
|
|
9
10
|
const isTurboModuleEnabled =
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
// https://github.com/facebook/react-native/pull/48362
|
|
12
|
+
(global as any).__turboModuleProxy || (global as any).RN$Bridgeless;
|
|
12
13
|
|
|
13
14
|
export const PushyModule =
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
Platform.OS === 'web'
|
|
16
|
+
? emptyModule
|
|
17
|
+
: isTurboModuleEnabled
|
|
18
|
+
? require('./NativePushy').default
|
|
19
|
+
: NativeModules.Pushy;
|
|
19
20
|
|
|
20
21
|
export const UpdateModule = PushyModule;
|
|
21
22
|
|
|
22
23
|
if (!PushyModule) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
throw Error(
|
|
25
|
+
'Failed to load react-native-update native module, please try to recompile',
|
|
26
|
+
);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
const PushyConstants = isTurboModuleEnabled
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
? PushyModule.getConstants()
|
|
31
|
+
: PushyModule;
|
|
31
32
|
|
|
32
33
|
export const downloadRootDir: string = PushyConstants.downloadRootDir;
|
|
33
34
|
export const packageVersion: string = PushyConstants.packageVersion;
|
|
34
35
|
export const currentVersion: string = PushyConstants.currentVersion;
|
|
35
36
|
|
|
36
37
|
export function setLocalHashInfo(hash: string, info: Record<string, any>) {
|
|
37
|
-
|
|
38
|
+
PushyModule.setLocalHashInfo(hash, JSON.stringify(info));
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
const currentVersionInfoString: string = PushyConstants.currentVersionInfo;
|
|
41
42
|
let _currentVersionInfo: Record<string, any> = {};
|
|
42
43
|
let isDebugChannel = false;
|
|
43
44
|
if (currentVersionInfoString) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
try {
|
|
46
|
+
_currentVersionInfo = JSON.parse(currentVersionInfoString);
|
|
47
|
+
if (_currentVersionInfo.debugChannel) {
|
|
48
|
+
isDebugChannel = true;
|
|
49
|
+
delete _currentVersionInfo.debugChannel;
|
|
50
|
+
setLocalHashInfo(currentVersion, _currentVersionInfo);
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
error(
|
|
54
|
+
i18n.t('error_parse_version_info', { info: currentVersionInfoString }),
|
|
55
|
+
);
|
|
56
|
+
}
|
|
56
57
|
}
|
|
57
58
|
export const currentVersionInfo = _currentVersionInfo;
|
|
58
59
|
|
|
@@ -64,32 +65,39 @@ export const isRolledBack: boolean = !!rolledBackVersion;
|
|
|
64
65
|
export const buildTime: string = PushyConstants.buildTime;
|
|
65
66
|
let uuid = PushyConstants.uuid;
|
|
66
67
|
|
|
67
|
-
|
|
68
68
|
async function getLocalHashInfo(hash: string) {
|
|
69
|
-
|
|
69
|
+
return JSON.parse(await PushyModule.getLocalHashInfo(hash));
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
// @deprecated use currentVersionInfo instead
|
|
73
73
|
export async function getCurrentVersionInfo(): Promise<{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
name?: string;
|
|
75
|
+
description?: string;
|
|
76
|
+
metaInfo?: string;
|
|
77
77
|
}> {
|
|
78
|
-
|
|
78
|
+
return currentVersion ? (await getLocalHashInfo(currentVersion)) || {} : {};
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
export const pushyNativeEventEmitter = new NativeEventEmitter(PushyModule);
|
|
82
82
|
|
|
83
83
|
if (!uuid) {
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
uuid = require('nanoid/non-secure').nanoid();
|
|
85
|
+
PushyModule.setUuid(uuid);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
log('uuid: ' + uuid);
|
|
89
|
-
|
|
90
88
|
export const cInfo = {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
rnu: require('../package.json').version,
|
|
90
|
+
rn: RNVersion,
|
|
91
|
+
os: `${Platform.OS} ${Platform.Version}`,
|
|
92
|
+
uuid,
|
|
95
93
|
};
|
|
94
|
+
|
|
95
|
+
log('bootup status', {
|
|
96
|
+
packageVersion,
|
|
97
|
+
currentVersion,
|
|
98
|
+
currentVersionInfo,
|
|
99
|
+
isFirstTime,
|
|
100
|
+
isFirstTimeDebug,
|
|
101
|
+
isDebugChannel,
|
|
102
|
+
cInfo,
|
|
103
|
+
});
|