Skip to content

fix(harmony): 修复首次加载标记重复消费导致误触发 rollback#586

Open
liusanhong wants to merge 1 commit into
reactnativecn:masterfrom
liusanhong:master
Open

fix(harmony): 修复首次加载标记重复消费导致误触发 rollback#586
liusanhong wants to merge 1 commit into
reactnativecn:masterfrom
liusanhong:master

Conversation

@liusanhong

@liusanhong liusanhong commented Jun 29, 2026

Copy link
Copy Markdown
  • 新增 consumeFirstLoadMarker,消费后清除标记,避免同一进程多次解析 bundleURL 误触发 rollback
  • 新增 ignoreRollback 标志位,对齐安卓 UpdateContext.java 同名逻辑
  • resolveLaunch 接入 ignoreRollback 参数,switchVersion 切换版本时重置该标志
  • clearFirstTime 与 persistState 维护 firstLoadMarked 标记同批原子落盘

Committed at: 2026-06-29 18:46
Committed by: liuqiang

Summary by CodeRabbit

  • Bug Fixes
    • Improved first-launch handling so the app remembers when the initial load has been consumed.
    • Made version switching and bundle URL resolution more reliable by reducing repeated rollback-triggered behavior in the same session.
    • Fixed the order of state updates so launch and update status are saved more consistently.

- 新增 consumeFirstLoadMarker,消费后清除标记,避免同一进程多次解析 bundleURL 误触发 rollback
- 新增 ignoreRollback 标志位,对齐安卓 UpdateContext.java 同名逻辑
- resolveLaunch 接入 ignoreRollback 参数,switchVersion 切换版本时重置该标志
- clearFirstTime 与 persistState 维护 firstLoadMarked 标记同批原子落盘

Committed at: 2026-06-29 18:46
Committed by: liuqiang

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

UpdateContext gains a persistent KEY_FIRST_LOAD_MARKED preferences key, an ignoreRollback instance flag, and a new consumeFirstLoadMarker() method that reads and clears the marker atomically. getBundleUrl(), clearFirstTime(), and switchVersion() are updated to coordinate these values. PushyTurboModule.getConstants() switches from isFirstTime() to consumeFirstLoadMarker().

Changes

First-load marker and ignoreRollback coordination

Layer / File(s) Summary
UpdateContext: marker, ignoreRollback, and method changes
harmony/pushy/src/main/ets/UpdateContext.ts
Adds KEY_FIRST_LOAD_MARKED static key and ignoreRollback field; adds consumeFirstLoadMarker() that reads, deletes, flushes, and returns the marker; clearFirstTime() now deletes the marker before the native operation; switchVersion() resets ignoreRollback = false; getBundleUrl() passes ignoreRollback to runStateCore, persists the marker when consumedFirstTime, then sets ignoreRollback = true.
PushyTurboModule: wire consumeFirstLoadMarker
harmony/pushy/src/main/ets/PushyTurboModule.ts
getConstants() calls context.consumeFirstLoadMarker() instead of context.isFirstTime(), so the marker is consumed on each constants read.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 Hop once, mark the trail,
Then consume the breadcrumb left behind.
ignoreRollback flips to true without fail,
The first-load flag cleared by design.
One nibble of marker, then off I go—
No double-rollbacks to steal my show! 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly matches the PR's main change: preventing repeated first-load marker consumption that could trigger rollback.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

harmony/pushy/src/main/ets/PushyTurboModule.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: .eslintrc.js » @react-native/eslint-config#overrides[4]:
Environment key "jest/globals" is unknown

at /node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2079:23
at Array.forEach (<anonymous>)
at ConfigValidator.validateEnvironment (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2073:34)
at ConfigValidator.validateConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2223:18)
at CascadingConfigArrayFactory._finalizeConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3985:23)
at CascadingConfigArrayFactory.getConfigArrayForFile (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3791:21)
at FileEnumerator._iterateFilesWithFile (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:368:43)
at FileEnumerator._iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:349:25)
at FileEnumerator.iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:299:59)
at iterateFiles.next (<anonymous>)
harmony/pushy/src/main/ets/UpdateContext.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: .eslintrc.js » @react-native/eslint-config#overrides[4]:
Environment key "jest/globals" is unknown

at /node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2079:23
at Array.forEach (<anonymous>)
at ConfigValidator.validateEnvironment (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2073:34)
at ConfigValidator.validateConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2223:18)
at CascadingConfigArrayFactory._finalizeConfigArray (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3985:23)
at CascadingConfigArrayFactory.getConfigArrayForFile (/node_modules/.pnpm/@eslint+eslintrc@2.1.4/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3791:21)
at FileEnumerator._iterateFilesWithFile (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:368:43)
at FileEnumerator._iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:349:25)
at FileEnumerator.iterateFiles (/node_modules/.pnpm/eslint@8.57.1/node_modules/eslint/lib/cli-engine/file-enumerator.js:299:59)
at iterateFiles.next (<anonymous>)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
harmony/pushy/src/main/ets/PushyTurboModule.ts (1)

78-85: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Consume the marker only after fallible constants work succeeds.

Line 78 deletes the first-load marker before clearRollbackMark() runs. If that later state operation throws, getConstants() will not return to JS, but the marker is already gone, so a retry reports isFirstTime: false.

Proposed fix
-    const isFirstTime = this.context.consumeFirstLoadMarker();
     const rolledBackVersion = this.context.rolledBackVersion();
     const uuid = this.context.getKv('uuid');
     const isUsingBundleUrl = this.context.getIsUsingBundleUrl();
 
     if (rolledBackVersion) {
       this.context.clearRollbackMark();
     }
+
+    const isFirstTime = this.context.consumeFirstLoadMarker();
🤖 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/PushyTurboModule.ts` around lines 78 - 85, Move
the first-load marker consumption in PushyTurboModule.getConstants so it happens
only after the fallible state work succeeds, especially after
clearRollbackMark() in the rolledBackVersion branch. Use the existing symbols
consumeFirstLoadMarker(), clearRollbackMark(), and getConstants() to reorder the
logic so a throw leaves the marker intact for retry.
🤖 Prompt for all review comments with 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.

Inline comments:
In `@harmony/pushy/src/main/ets/UpdateContext.ts`:
- Around line 29-31: Make `ignoreRollback` process-wide instead of being reset
per `UpdateContext` instance; the current `private ignoreRollback = false` in
`UpdateContext` lets a new context in the same process re-enable rollback
checks. Update the `UpdateContext` class so the flag is shared across instances
(or stored on a process-level singleton/static), and ensure `getBundleUrl()` and
the rollback path around `shouldRollback`/`firstTime` consult and update that
shared state consistently so a second resolution in the same process cannot
trigger rollback again.

---

Outside diff comments:
In `@harmony/pushy/src/main/ets/PushyTurboModule.ts`:
- Around line 78-85: Move the first-load marker consumption in
PushyTurboModule.getConstants so it happens only after the fallible state work
succeeds, especially after clearRollbackMark() in the rolledBackVersion branch.
Use the existing symbols consumeFirstLoadMarker(), clearRollbackMark(), and
getConstants() to reorder the logic so a throw leaves the marker intact for
retry.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1944a829-970a-4b7c-aec7-56fad44cedaa

📥 Commits

Reviewing files that changed from the base of the PR and between 38717fa and 9e76322.

📒 Files selected for processing (2)
  • harmony/pushy/src/main/ets/PushyTurboModule.ts
  • harmony/pushy/src/main/ets/UpdateContext.ts

Comment on lines +29 to +31
// 对齐安卓 UpdateContext.java:30。bundleURL 可能在同一进程内被解析多次,
// 首次消费 firstTime 后置 true,避免后续解析误触发 rollback。
private ignoreRollback = false;

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make ignoreRollback process-wide, not instance-local.

Line 31 resets to false for every new UpdateContext, but the provider path constructs a new UpdateContext before resolving getBundleUrl(). A second resolution through a new context in the same process can still pass false at Line 392 and re-trigger the rollback this PR is trying to suppress.

Proposed fix
-  private ignoreRollback = false;
+  private static ignoreRollback = false;
...
-      this.ignoreRollback = false;
+      UpdateContext.ignoreRollback = false;
...
-      this.ignoreRollback,
+      UpdateContext.ignoreRollback,
...
-      this.ignoreRollback = true;
+      UpdateContext.ignoreRollback = true;

Also applies to: 379-405

🤖 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 29 - 31, Make
`ignoreRollback` process-wide instead of being reset per `UpdateContext`
instance; the current `private ignoreRollback = false` in `UpdateContext` lets a
new context in the same process re-enable rollback checks. Update the
`UpdateContext` class so the flag is shared across instances (or stored on a
process-level singleton/static), and ensure `getBundleUrl()` and the rollback
path around `shouldRollback`/`firstTime` consult and update that shared state
consistently so a second resolution in the same process cannot trigger rollback
again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant