-
-
Notifications
You must be signed in to change notification settings - Fork 659
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add fully automatic add-on updates #17682
base: master
Are you sure you want to change the base?
Conversation
@coderabbitai review |
WalkthroughThe changes modify the update logic for NVDA add-ons across several modules. The _DataManager._addonsPendingUpdate method now accepts an optional error callback and tracks pending updates using a dictionary. The status module has been refactored with a new _canUpdateAddon function to determine update eligibility via type checks and version comparisons. Configuration files have been updated to activate an automatic update option and introduce an allowIncompatibleUpdates setting. GUI components are enhanced with improved error handling, background update processing via threading, and refined message box management. Scheduling logic and tests now incorporate a new start offset, and the user guide documentation has been updated accordingly. Changes
Sequence Diagram(s)sequenceDiagram
participant U as UpdatableAddonsDialog
participant D as _DataManager
participant M as Status Module (_canUpdateAddon)
participant BG as Background Thread
participant E as Error Handler
U->>D: Call _addonsPendingUpdate(onDisplayableError)
D->>M: Check addon update status
M-->>D: Return update status
D-->>U: Return dict of pending updates
U->>U: Evaluate automaticUpdates setting (match statement)
alt Automatic Update
U->>BG: Spawn background _updateAddons(addons)
else Notification
U->>U: Display update notification (with error handling)
end
BG->>E: If error occurs, call handleDisplayableError
Assessment against linked issues
Possibly related PRs
Suggested labels
Suggested reviewers
✨ Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (5)
source/addonStore/dataManager.py (1)
355-358
: Consider adding a docstring.
A brief docstring would help explain the new “onDisplayableError” parameter and its usage in this method.source/gui/addonStoreGui/controls/messageDialogs.py (1)
601-624
: Background update approach may benefit from event-driven logic.
Currently, the loop uses a short sleep, which can be acceptable in smaller codebases. An event or condition-based wait could improve performance in more significant scenarios. Otherwise, the logic and concurrency approach is valid.user_docs/en/userGuide.md (3)
3079-3086
: Verify options list formattingThe options list should be consistently formatted. Consider using a bullet list instead of a table for better readability.
-|Options |Notify (Default), Update Automatically, Disabled | -|Default |Notify | +Options: +* Notify (Default) +* Update Automatically +* Disabled
3088-3106
: Verify behavior table formattingThe behavior table should be consistently formatted with other similar tables in the documentation. Consider adding a header row.
-|Option |Behaviour | -|---|---| +| Option | Behavior | +|---------|-----------|
3110-3117
: Verify cross-reference linksThe text references "automatic updates" but doesn't include a link to the relevant section. Consider adding a cross-reference link for better navigation.
-Enabling this may be useful for switching over to using add-on breaking releases (the first release of the year). +Enabling this may be useful for switching over to using add-on breaking releases (the first release of the year). See [Automatic add-on updates](#AutomaticAddonUpdates) for more information.
📜 Review details
Configuration used: .coderabbit.yml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
source/addonStore/dataManager.py
(3 hunks)source/addonStore/models/status.py
(3 hunks)source/config/configFlags.py
(2 hunks)source/config/configSpec.py
(1 hunks)source/gui/addonStoreGui/controls/messageDialogs.py
(5 hunks)source/gui/message.py
(3 hunks)source/gui/settingsDialogs.py
(3 hunks)source/utils/schedule.py
(2 hunks)tests/unit/test_util/test_schedule.py
(2 hunks)user_docs/en/userGuide.md
(2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
`**/*`: Focus on code smells, logic errors, edge cases, mis...
**/*
: Focus on code smells, logic errors, edge cases, missing test cases, security flaws and serious issues. Avoid commenting on minor issues such as linting, formatting and style issues. This project uses tabs instead of spaces, do not suggest usage of spaces over tabs. Are there any 'red flags' in this code that might warrant closer investigation from a security standpoint? Explain what makes them suspicious. When providing code suggestions, particularly when requested, ensure GitHub's suggestion format is used, i.e.:suggestion <code changes>
source/config/configFlags.py
tests/unit/test_util/test_schedule.py
source/gui/message.py
source/config/configSpec.py
source/utils/schedule.py
source/addonStore/models/status.py
source/gui/addonStoreGui/controls/messageDialogs.py
source/addonStore/dataManager.py
user_docs/en/userGuide.md
source/gui/settingsDialogs.py
`**/*.py`: _, pgettext, ngettext, and ngettext are defined ...
**/*.py
: _, pgettext, ngettext, and ngettext are defined globally, errors for this being undefined can be ignored.
source/config/configFlags.py
tests/unit/test_util/test_schedule.py
source/gui/message.py
source/config/configSpec.py
source/utils/schedule.py
source/addonStore/models/status.py
source/gui/addonStoreGui/controls/messageDialogs.py
source/addonStore/dataManager.py
source/gui/settingsDialogs.py
`**/*.md`: Focus on grammar, spelling, and punctuation. Als...
**/*.md
: Focus on grammar, spelling, and punctuation. Also consider readability and clarity of contents. Ensure that changes follow the style of one sentence per line, suggest changes if this is not met.
user_docs/en/userGuide.md
📓 Learnings (1)
source/config/configFlags.py (1)
Learnt from: seanbudd
PR: nvaccess/nvda#16636
File: source/config/configFlags.py:209-209
Timestamp: 2024-11-12T04:38:46.468Z
Learning: The `UPDATE` state in the `AddonsAutomaticUpdate` class is commented out and should be implemented in the future, specifically linked to issue #3208.
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Analyze (python)
🔇 Additional comments (29)
source/utils/schedule.py (2)
76-80
: LGTM! Well-documented constant with clear purpose.The
START_MINUTE_OFFSET
constant provides a sensible 1-minute delay for the first scheduled job after NVDA starts, allowing time for initialization.
96-100
: Verify the offset calculation for edge cases.The offset calculation looks correct, but let's verify it handles all edge cases properly:
- When
startTime.minute
is close to 60- When multiple jobs are scheduled (
scheduledDailyJobCount > 0
)✅ Verification successful
🏁 Script executed:
#!/bin/bash # Description: Search for any reported issues or bugs related to job scheduling timing # Look for patterns like "schedule", "timing", "delay", "offset" in issue comments and commit messages rg -i "schedule|timing|delay|offset" $(git log --pretty=format:%H -n 100 | xargs -I{} echo "{}^!")Length of output: 0
I'll gather additional context on the offset calculation by inspecting the relevant file and the definitions of the constants. Let's run a new shell script that prints the surrounding code from source/utils/schedule.py (lines 80–130) and also searches for the definitions of both START_MINUTE_OFFSET and DAILY_JOB_MINUTE_OFFSET.
🏁 Script executed:
#!/bin/bash echo "------ Displaying lines 80-130 from source/utils/schedule.py ------" sed -n '80,130p' source/utils/schedule.py echo "------ Searching for START_MINUTE_OFFSET definitions ------" rg "START_MINUTE_OFFSET" -n . echo "------ Searching for DAILY_JOB_MINUTE_OFFSET definitions ------" rg "DAILY_JOB_MINUTE_OFFSET" -n .Length of output: 3408
Offset calculation handles edge cases correctly.
After reviewing the _calculateDailyTimeOffset() implementation and the associated constants (START_MINUTE_OFFSET = 1 and DAILY_JOB_MINUTE_OFFSET = 3) along with the rollover logic that adjusts both minutes (using % 60) and hours (by adding the quotient and taking % 24), it’s clear that:
- Minutes near 60 are correctly rolled over into hours.
- Increasing scheduledDailyJobCount (i.e. adding multiples of DAILY_JOB_MINUTE_OFFSET) is properly accounted for by the modulo operations.
Additionally, unit tests in tests/unit/test_util/test_schedule.py verify these behaviors, ensuring that expected offsets (e.g., for minute values near 60) are returned correctly.
tests/unit/test_util/test_schedule.py (4)
27-28
: LGTM! Good refactoring to reduce duplication.The
TODAY_AT_MIDNIGHT
constant effectively centralizes the midnight time reference used across multiple test methods.
121-127
: LGTM! Test verifies the initial job delay.The test correctly verifies that the first job is scheduled with the new
START_MINUTE_OFFSET
delay.
128-137
: LGTM! Test verifies subsequent job scheduling.The test correctly verifies that the second job is scheduled with both
START_MINUTE_OFFSET
andDAILY_JOB_MINUTE_OFFSET
delays.
138-156
: LGTM! Comprehensive edge case testing.The tests thoroughly verify:
- Minute overflow (11:59 → 12:xx)
- Day overflow (23:59 → 00:xx)
source/addonStore/dataManager.py (3)
40-40
: Import usage looks fine.
No issues found with the newly imported symbols.
360-365
: Logic for allowing incompatible updates looks correct.
The condition seamlessly switches between fetching latest vs. compatible add-ons based on the user’s config.
381-388
:⚠️ Potential issueDuplicate assignment likely indicates a logic error.
When an add-on name already exists in the dictionary, lines 385 and 387 both overwrite it unconditionally. This defeats the purpose of the if-check on line 384. To retain only the newer add-on when '_canUpdateAddon' is true, consider:if addon.name in addonsPendingUpdate: if _canUpdateAddon(addon, addonsPendingUpdate[addon.name]): addonsPendingUpdate[addon.name] = addon else: addonsPendingUpdate[addon.name] = addonLikely invalid or redundant comment.
source/addonStore/models/status.py (4)
2-2
: No specific issues with updated copyright.
26-26
: No issues with expanded import.
246-281
: Update check logic looks good overall.
The function correctly distinguishes between store-based and side-loaded add-ons, returning None when version parsing fails.
283-330
: Overall update status logic is appropriate.
Correctly interprets the result of '_canUpdateAddon' and provides relevant statuses, including REPLACE_SIDE_LOAD for unknown version comparisons.source/gui/addonStoreGui/controls/messageDialogs.py (7)
6-7
: New imports appear consistent with usage.
Imports for threading, sleep, and additional message handling are aligned with the new background updates functionality.Also applies to: 35-35, 38-38
372-372
: New class-level error callback definition is clear.
This provides a convenient hook for error handling in background operations.
558-560
: Static method for error handling is straightforward.
Properly invokes 'displayError' on the main frame.
571-574
: Registering/unregistering callbacks is cleanly done.
This ensures consistent handling of displayable errors within a specific scope.
575-578
: Early return when no updates are found is sensible.
Prevents unnecessary UI prompts or threads from being started when there’s nothing to update.
579-580
: Logging found updates is straightforward.
No issues identified.
581-599
: Match statement cleanly handles various update strategies.
Logic for NOTIFY vs. UPDATE vs. fallback error is well-structured.source/config/configFlags.py (1)
238-248
: LGTM! Implementation of automatic updates feature.The changes correctly implement the
UPDATE
member in theAddonsAutomaticUpdate
class, which was previously commented out. This aligns with the PR objectives to enable automatic add-on updates.source/config/configSpec.py (1)
339-340
: LGTM! Configuration options for automatic updates.The changes correctly add:
- "update" as a valid option for
automaticUpdates
allowIncompatibleUpdates
setting to control incompatible add-on updatesThis aligns with the PR objectives to enable automatic updates and provide control over incompatible add-on updates.
source/gui/message.py (2)
55-74
: LGTM! Thread-safe message box counter management.The new
_countAsMessageBox
decorator correctly manages the message box counter in a thread-safe way:
- Uses
_messageBoxCounterLock
for thread safety- Ensures counter is decremented in
finally
block even if an exception occurs- Properly wraps the decorated function using
wraps
to preserve metadata
77-77
: LGTM! Decorator application.The
displayDialogAsModal
function is correctly decorated with@_countAsMessageBox()
to manage the message box counter.source/gui/settingsDialogs.py (3)
3247-3247
: LGTM! Label updated to reflect automatic updates functionality.The label change from "Update notifications" to "Automatic updates" better reflects the new automatic add-on update functionality.
3271-3277
: LGTM! New checkbox added for incompatible add-on updates.The implementation:
- Adds UI control with clear label
- Properly binds help event
- Correctly initializes from config value
3354-3354
: LGTM! Config value properly saved.The new incompatible updates setting is correctly saved to the configuration.
user_docs/en/userGuide.md (2)
3074-3076
: Added new automatic update option for add-onsThe documentation introduces a new "Update Automatically" option for add-on updates, which will automatically update add-ons in the background and prompt for an NVDA restart when updates are complete.
3110-3117
: Added new setting for allowing incompatible add-on updatesThe documentation adds a new setting "Allow automatic updates to install incompatible add-ons" which enables automatic updates to add-ons that may not be fully compatible with the current NVDA version. The explanation clearly outlines that this is disabled by default and explains the use case for alpha/beta testers.
In #3208, there have been security concerns regarding automatic update. How are they taken in consideration here? Given add-ons have a very wide access to the user's system, we can imagine such a attack:
This risk already exists in NVDA, and users are warned to install only add-ons they trust. But automatic update makes the risk more important IMO. In comparison, browser extensions, that may also been updated automatically, have not such a wide access to the system. If NV Access chooses to keep this possibility anyway, I'd suggest:
|
I don't think it's a serious security risk - users are just as likely to click through the notification or manually install a malicious add-on in these cases. You raise a good point about the restart notification, but I think a message after installation is equally as invasive as the update notification itself. |
If we accept the risk of current add-on framework (as today) and their automatic update, the best UX would be to use Windows notifications IMO. People were already used to this in @josephsl's Add-on Updater. Unfortunately, Windows notifications are not available in Windows 8.1 so a fallback solution would be needed for people running this OS. Also, one interesting point going with automatic add-on update, would be to store somewhere the last install date of an add-on. This way, people experimenting a regression or strange behaviour could look at add-on install dates and check if one matches the period they noticed the unexpected behaviour. |
For the Add-on of Enabled (incompatible) it will be disabled after update, I think it should be kept enabled, or the settings should be allowed to remain enabled, otherwise it will lose the meaning of allowing automatic update of these Add-ons |
It is recommended to list the updated Add-ons information in the dialog box that prompts to restart after the Add-ons update is completed, so that users can easily understand which Add-ons have been updated. |
Link to issue number:
Closes #3208
Closes #17162
Summary of the issue:
To update add-ons automatically, users must click-through a dialog to confirm they wish to update all add-ons.
Instead, this can be done in the background, and the user just needs to confirm whether to restart NVDA once add-on updates are complete.
Additionally:
Description of user facing changes
This can be changed in the "automatic updates" section.
Description of development approach
Testing strategy:
Known issues with pull request:
None
Code Review Checklist:
Summary by CodeRabbit