Skip to content

Add version check to UI#691

Open
devksingh4 wants to merge 3 commits intomainfrom
version-check
Open

Add version check to UI#691
devksingh4 wants to merge 3 commits intomainfrom
version-check

Conversation

@devksingh4
Copy link
Copy Markdown
Contributor

@devksingh4 devksingh4 commented Apr 30, 2026

Generate a version.json file in the UI - have the UI poll for a change to this file and prompt the user to refresh if there's a change.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added automatic version polling that notifies users when app updates are available with a one-click refresh option
    • Polling resumes when the app becomes visible after being backgrounded
  • Chores

    • Bumped version to 4.9.7
  • Tests

    • Added comprehensive test suite for version checking functionality

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

Walkthrough

This pull request introduces a client-side version polling mechanism to detect and notify users of application updates. The workflow injects a VITE_BUILD_HASH environment variable, which is embedded into a generated version.json asset during build via a custom Vite plugin. A new versionCheck module polls this endpoint every 5 minutes (or when the document becomes visible), compares the remote version against the local build hash, and triggers either a callback or forced refresh with cache clearing when an update is detected. The App component integrates this polling and displays an update notification using Mantine's notification API. The package version is bumped to 4.9.7. Comprehensive tests validate polling behavior, failure handling, visibility-based rechecks, and cache clearing.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Detailed assessment

The changes introduce a new feature spanning multiple concerns: CI/CD configuration, Vite build plugin, async polling logic with failure counting, DOM API integration (visibility listener), cache storage manipulation, and React component integration. While each individual section is relatively straightforward, the heterogeneity of changes (workflow config, plugin code, module-level logic, component hooks, and test suite) and the interaction between these layers require distributed reasoning across different file types and architectural concerns. The comprehensive test suite (268 lines) demonstrates good coverage but itself requires careful review.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add version check to UI' directly summarizes the main objective: implementing version polling and update detection in the UI component, which is the primary focus across all modified files.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch version-check

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
Review rate limit: 0/1 reviews remaining, refill in 60 minutes.

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

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/ui/App.tsx`:
- Around line 23-40: The effect calls startVersionPolling which sets up
long-lived polling/listeners but never cleans them up; change
startVersionPolling to return a stop/cleanup function (e.g., stopVersionPolling)
and update the useEffect in App.tsx to capture that return value and return it
as the cleanup (return () => stop()). Keep existing callback that calls
notifications.show and preserve forceRefresh usage when wiring the cleanup.

In `@src/ui/versionCheck.ts`:
- Around line 44-49: The forceRefresh function currently calls
window.location.reload() without awaiting cache deletions; make forceRefresh
async, await caches.keys(), map those keys to caches.delete(k) and await
Promise.all(...) so all deletions complete before calling
window.location.reload(), referencing the forceRefresh function and the
caches.keys()/caches.delete calls to locate where to add the awaits.
- Around line 35-38: The failure cutoff is off-by-one: the condition uses
fetchFailures > 10 which triggers on the 11th failure; change the conditional in
the version polling logic to use fetchFailures >= 10 so stopPolling() is invoked
after ten consecutive failures (update the conditional that references
fetchFailures and stopPolling in versionCheck.ts accordingly).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 838cd2c7-98ce-4572-8cf6-b5a212e827ed

📥 Commits

Reviewing files that changed from the base of the PR and between 1c55bc7 and 5580989.

📒 Files selected for processing (6)
  • .github/workflows/deploy-qa.yml
  • package.json
  • src/ui/App.tsx
  • src/ui/versionCheck.test.ts
  • src/ui/versionCheck.ts
  • src/ui/vite.config.mjs

Comment thread src/ui/App.tsx
Comment on lines +23 to +40
useEffect(() => {
startVersionPolling(() => {
notifications.show({
id: "version-update",
title: "Update available",
message: (
<Group gap="xs" mt={4}>
<Text size="sm">A new version of the app is available.</Text>
<Button size="xs" variant="light" onClick={forceRefresh}>
Refresh to update
</Button>
</Group>
),
color: "blue",
autoClose: 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.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Return a cleanup from the polling effect.

startVersionPolling() registers long-lived state, but this effect never tears it down. In React Strict Mode or on remount, that can leave duplicate timers/listeners and duplicate update notifications.

Have the helper return a stop function and return it from this effect.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/App.tsx` around lines 23 - 40, The effect calls startVersionPolling
which sets up long-lived polling/listeners but never cleans them up; change
startVersionPolling to return a stop/cleanup function (e.g., stopVersionPolling)
and update the useEffect in App.tsx to capture that return value and return it
as the cleanup (return () => stop()). Keep existing callback that calls
notifications.show and preserve forceRefresh usage when wiring the cleanup.

Comment thread src/ui/versionCheck.ts
Comment on lines +35 to +38
if (fetchFailures > 10) {
console.warn("Failed to fetch version 10 times; giving up.");
stopPolling();
} else {
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the failure threshold off by one.

fetchFailures > 10 stops on the 11th rejected request, not the 10th. If the intended cutoff is 10 consecutive failures, this should be >= 10.

🛠️ Suggested fix
-    if (fetchFailures > 10) {
+    if (fetchFailures >= 10) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (fetchFailures > 10) {
console.warn("Failed to fetch version 10 times; giving up.");
stopPolling();
} else {
if (fetchFailures >= 10) {
console.warn("Failed to fetch version 10 times; giving up.");
stopPolling();
} else {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/versionCheck.ts` around lines 35 - 38, The failure cutoff is
off-by-one: the condition uses fetchFailures > 10 which triggers on the 11th
failure; change the conditional in the version polling logic to use
fetchFailures >= 10 so stopPolling() is invoked after ten consecutive failures
(update the conditional that references fetchFailures and stopPolling in
versionCheck.ts accordingly).

Comment thread src/ui/versionCheck.ts
Comment on lines +44 to +49
export function forceRefresh() {
if ("caches" in window) {
caches.keys().then((keys) => keys.forEach((k) => caches.delete(k)));
}
window.location.reload();
}
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Await cache deletion before reloading.

window.location.reload() runs before the Cache Storage deletions finish, so stale assets can survive the refresh. Make this async and wait for the deletions to complete first.

♻️ Suggested fix
-export function forceRefresh() {
-  if ("caches" in window) {
-    caches.keys().then((keys) => keys.forEach((k) => caches.delete(k)));
-  }
-  window.location.reload();
+export async function forceRefresh() {
+  if ("caches" in window) {
+    const keys = await caches.keys();
+    await Promise.all(keys.map((k) => caches.delete(k)));
+  }
+  window.location.reload();
 }
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 48-48: Prefer globalThis over window.

See more on https://sonarcloud.io/project/issues?id=acm-uiuc_core&issues=AZ3gwzYRNLg40HRg8O4S&open=AZ3gwzYRNLg40HRg8O4S&pullRequest=691


[warning] 45-45: Prefer globalThis over window.

See more on https://sonarcloud.io/project/issues?id=acm-uiuc_core&issues=AZ3gwzYRNLg40HRg8O4R&open=AZ3gwzYRNLg40HRg8O4R&pullRequest=691

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/ui/versionCheck.ts` around lines 44 - 49, The forceRefresh function
currently calls window.location.reload() without awaiting cache deletions; make
forceRefresh async, await caches.keys(), map those keys to caches.delete(k) and
await Promise.all(...) so all deletions complete before calling
window.location.reload(), referencing the forceRefresh function and the
caches.keys()/caches.delete calls to locate where to add the awaits.

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