Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions harmony/pushy/src/main/ets/PushyFileJSBundleProvider.ets
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,35 @@ import { UpdateContext } from './UpdateContext';

export class PushyFileJSBundleProvider extends JSBundleProvider {
private updateContext: UpdateContext;
private path: string = '';

constructor(context: common.UIAbilityContext) {
super();
this.updateContext = new UpdateContext(context);
this.path = this.updateContext.getBundleUrl();
}

getURL(): string {
return this.path;
return this.updateContext.getBundleUrl();
}

async getBundle(): Promise<FileJSBundle> {
if (!this.path) {
const path = this.updateContext.getBundleUrl();
if (!path) {
throw new JSBundleProviderError({
whatHappened: 'No pushy bundle found. using default bundle',
howCanItBeFixed: [''],
});
}
try {
await fs.access(this.path, fs.OpenMode.READ_ONLY);
await fs.access(path, fs.OpenMode.READ_ONLY);
return {
filePath: this.path,
filePath: path,
};
} catch (error) {
throw new JSBundleProviderError({
whatHappened: `Couldn't load JSBundle from ${this.path}`,
whatHappened: `Couldn't load JSBundle from ${path}`,
extraData: error,
howCanItBeFixed: [
`Check if a bundle exists at "${this.path}" on your device.`,
`Check if a bundle exists at "${path}" on your device.`,
],
});
}
Expand Down
17 changes: 14 additions & 3 deletions harmony/pushy/src/main/ets/PushyTurboModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,17 @@ export class PushyTurboModule extends UITurboModule {
await this.mUiCtx.startAbility(want);
}

private async reloadBridge(): Promise<void> {
const devToolsController = (this.ctx as Record<string, any>).devToolsController;
if (devToolsController) {
logger.debug(TAG, 'reloadBridge via devToolsController RELOAD');
devToolsController.eventEmitter.emit("RELOAD", { reason: 'HotReload2' });
} else {
logger.debug(TAG, 'reloadBridge via restartAbility');
await this.restartAbility();
}
}

getConstants(): Object {
logger.debug(TAG, ',call getConstants');
const packageVersion = this.context.getPackageVersion();
Expand All @@ -75,7 +86,7 @@ export class PushyTurboModule extends UITurboModule {
const currentVersionInfo = currentVersion
? this.context.getKv(`hash_${currentVersion}`)
: '';
const isFirstTime = this.context.isFirstTime();
const isFirstTime = this.context.consumeFirstLoadMarker();
const rolledBackVersion = this.context.rolledBackVersion();
const uuid = this.context.getKv('uuid');
const isUsingBundleUrl = this.context.getIsUsingBundleUrl();
Expand Down Expand Up @@ -121,7 +132,7 @@ export class PushyTurboModule extends UITurboModule {

try {
this.context.switchVersion(hash);
await this.restartAbility();
await this.reloadBridge();
} catch (error) {
logger.error(TAG, `reloadUpdate failed: ${getErrorMessage(error)}`);
throw Error(`switchVersion failed ${getErrorMessage(error)}`);
Expand All @@ -131,7 +142,7 @@ export class PushyTurboModule extends UITurboModule {
async restartApp(): Promise<void> {
logger.debug(TAG, ',call restartApp');
try {
await this.restartAbility();
await this.reloadBridge();
} catch (error) {
logger.error(TAG, `restartApp failed: ${getErrorMessage(error)}`);
throw Error(`restartApp failed ${getErrorMessage(error)}`);
Expand Down
42 changes: 35 additions & 7 deletions harmony/pushy/src/main/ets/UpdateContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class UpdateContext {
private preferences!: preferences.Preferences;
private static DEBUG: boolean = false;
private static isUsingBundleUrl: boolean = false;
private static ignoreRollback: boolean = false;

constructor(context: common.UIAbilityContext) {
this.context = context;
Expand Down Expand Up @@ -72,9 +73,8 @@ export class UpdateContext {

public getBuildTime(): string {
try {
const content = this.context.resourceManager.getRawFileContentSync(
'meta.json',
);
const content =
this.context.resourceManager.getRawFileContentSync('meta.json');
const metaData = JSON.parse(
new util.TextDecoder().decodeToString(content),
) as Record<string, string | number | boolean | null | undefined>;
Expand Down Expand Up @@ -182,6 +182,8 @@ export class UpdateContext {
clearExisting?: boolean;
removeStaleHash?: boolean;
cleanUp?: boolean;
markFirstLoadMarker?: boolean;
clearFirstLoadMarker?: boolean;
} = {},
): void {
if (options.clearExisting) {
Expand All @@ -191,6 +193,12 @@ export class UpdateContext {
if (options.removeStaleHash && state.staleVersionToDelete) {
this.preferences.deleteSync(`hash_${state.staleVersionToDelete}`);
}
if (options.markFirstLoadMarker) {
this.preferences.putSync('firstLoadMarked', 'true');
}
if (options.clearFirstLoadMarker) {
this.preferences.deleteSync('firstLoadMarked');
}
this.flushPreferences('persist state');
if (options.cleanUp) {
this.cleanUp();
Expand All @@ -203,6 +211,7 @@ export class UpdateContext {
options: {
removeStaleHash?: boolean;
cleanUp?: boolean;
clearFirstLoadMarker?: boolean;
} = {},
): StateCoreResult {
const nextState = NativePatchCore.runStateCore(
Expand Down Expand Up @@ -245,6 +254,7 @@ export class UpdateContext {
return;
}

UpdateContext.ignoreRollback = false;
this.cleanUp();
this.persistState(nextState, { clearExisting: true });
}
Expand Down Expand Up @@ -278,7 +288,10 @@ export class UpdateContext {
}

public clearFirstTime(): void {
this.runStateOperation(STATE_OP_CLEAR_FIRST_TIME, '', { cleanUp: true });
this.runStateOperation(STATE_OP_CLEAR_FIRST_TIME, '', {
cleanUp: true,
clearFirstLoadMarker: true,
});
}

public clearRollbackMark(): void {
Expand Down Expand Up @@ -361,23 +374,38 @@ export class UpdateContext {
}

this.runStateOperation(STATE_OP_SWITCH_VERSION, hash);
UpdateContext.ignoreRollback = false;
} catch (e) {
console.error('Failed to switch version:', e);
throw e;
}
}

public consumeFirstLoadMarker(): boolean {
const marked = this.readString('firstLoadMarked') === 'true';
if (marked) {
this.preferences.deleteSync('firstLoadMarked');
this.flushPreferences('clear first load marker');
}
return marked;
}

public getBundleUrl() {
UpdateContext.isUsingBundleUrl = true;
const launchState = NativePatchCore.runStateCore(
STATE_OP_RESOLVE_LAUNCH,
this.getStateSnapshot(),
'',
false,
false,
UpdateContext.ignoreRollback,
true,
);
if (launchState.didRollback || launchState.consumedFirstTime) {
this.persistState(launchState);
this.persistState(launchState, {
markFirstLoadMarker: launchState.consumedFirstTime,
});
}
if (launchState.consumedFirstTime) {
UpdateContext.ignoreRollback = true;
Comment on lines 402 to +408

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Only mark first-load after the selected bundle is verified.

firstLoadMarked and ignoreRollback are set before the loop confirms launchState.loadVersion exists. If that file is missing, the later rollback path can load a fallback bundle while getConstants() still reports isFirstTime: true for the failed version.

Suggested direction
-    if (launchState.didRollback || launchState.consumedFirstTime) {
+    const shouldMarkFirstLoad = !!launchState.consumedFirstTime;
+    if (launchState.didRollback || shouldMarkFirstLoad) {
       this.persistState(launchState, {
-        markFirstLoadMarker: launchState.consumedFirstTime,
       });
     }
-    if (launchState.consumedFirstTime) {
-      UpdateContext.ignoreRollback = true;
-    }

Then set firstLoadMarked / ignoreRollback only immediately before returning a verified bundleFile, or clear both before calling rollBack() on the missing-file paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@harmony/pushy/src/main/ets/UpdateContext.ts` around lines 402 - 408,
`UpdateContext` is setting `firstLoadMarked` and `ignoreRollback` too early,
before `launchState.loadVersion` is confirmed and a verified bundle is returned.
Move the `persistState(...)` and `UpdateContext.ignoreRollback = true` logic so
it runs only after the selected bundle file has been validated and is about to
be returned, or explicitly clear both flags on the missing-file rollback paths
before calling `rollBack()`.

}

let version = launchState.loadVersion || '';
Expand Down