Implement check-for-updates and auto-update#1357
Conversation
55a4f8b to
acc46af
Compare
There was a problem hiding this comment.
Pull request overview
Adds macOS-native update support by downloading and installing a .dmg when available, while keeping existing Windows installer and Linux/PyPI (pip/uv) paths intact.
Changes:
- Implemented macOS DMG download/mount/copy/unmount update flow and integrated it into the update manager.
- Generalized URL and downloaded-file validation helpers for reuse across platforms.
- Expanded unit tests to cover macOS DMG helpers and the new update-manager macOS branching behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
ardupilot_methodic_configurator/backend_internet.py |
Adds macOS DMG install pipeline and shared validation helpers used by update flows. |
ardupilot_methodic_configurator/data_model_software_updates.py |
Routes macOS updates to DMG installer when present; otherwise falls back to pip. |
tests/unit_backend_internet.py |
Adds unit tests for new macOS DMG helpers and shared validation utilities. |
tests/unit_data_model_software_updates.py |
Adds tests for macOS update-manager behavior (DMG path, fallback, failures). |
ARCHITECTURE_1_software_update.md |
Documents new macOS DMG update approach and helper functions. |
tests/unit_backend_internet.py
Outdated
| ) | ||
|
|
||
| # pylint: disable=unused-argument | ||
| # pylint: disable=unused-argument, too-many-lines, redefined-outer-nam |
There was a problem hiding this comment.
The pylint directive is misspelled (redefined-outer-nam), so pylint won’t actually disable redefined-outer-name and CI/linting may fail. Rename it to redefined-outer-name.
| # pylint: disable=unused-argument, too-many-lines, redefined-outer-nam | |
| # pylint: disable=unused-argument, too-many-lines, redefined-outer-name |
| if progress_callback: | ||
| progress_callback(0.0, _("Installing {}...").format(app_name)) | ||
|
|
||
| # Use ditto to preserve metadata, resource forks, and extended attributes | ||
| subprocess.check_call(["ditto", app_path, dest]) # noqa: S603, S607 | ||
|
|
||
| if progress_callback: | ||
| progress_callback(100.0, _("Installation complete")) | ||
|
|
There was a problem hiding this comment.
Progress reporting is duplicated and potentially conflicting: _install_app_from_mount() emits 0.0 and 100.0, and _mount_and_install_dmg() also emits 0.0 and 100.0. This can lead to repeated 0%/100% updates with different messages. Consider centralizing progress reporting in one place (e.g., only in _mount_and_install_dmg()), or use distinct progress ranges per phase (mount/install/unmount) so progress is monotonic and messages aren’t overwritten at the same percentage.
| if progress_callback: | |
| progress_callback(0.0, _("Installing {}...").format(app_name)) | |
| # Use ditto to preserve metadata, resource forks, and extended attributes | |
| subprocess.check_call(["ditto", app_path, dest]) # noqa: S603, S607 | |
| if progress_callback: | |
| progress_callback(100.0, _("Installation complete")) | |
| # Use ditto to preserve metadata, resource forks, and extended attributes | |
| subprocess.check_call(["ditto", app_path, dest]) # noqa: S603, S607 |
| if progress_callback: | ||
| progress_callback(0.0, _("Mounting DMG...")) |
There was a problem hiding this comment.
Progress reporting is duplicated and potentially conflicting: _install_app_from_mount() emits 0.0 and 100.0, and _mount_and_install_dmg() also emits 0.0 and 100.0. This can lead to repeated 0%/100% updates with different messages. Consider centralizing progress reporting in one place (e.g., only in _mount_and_install_dmg()), or use distinct progress ranges per phase (mount/install/unmount) so progress is monotonic and messages aren’t overwritten at the same percentage.
| if result: | ||
| if progress_callback: | ||
| progress_callback(100.0, _("Installation complete. Please restart the application.")) | ||
| logging_info(_("macOS DMG installation complete. Please restart the application.")) |
There was a problem hiding this comment.
Progress reporting is duplicated and potentially conflicting: _install_app_from_mount() emits 0.0 and 100.0, and _mount_and_install_dmg() also emits 0.0 and 100.0. This can lead to repeated 0%/100% updates with different messages. Consider centralizing progress reporting in one place (e.g., only in _mount_and_install_dmg()), or use distinct progress ranges per phase (mount/install/unmount) so progress is monotonic and messages aren’t overwritten at the same percentage.
| def _validate_github_url(url: str) -> bool: | ||
| """Validate that URL is from a trusted GitHub source.""" | ||
| # This whitelist is intentionally restrictive for security reasons | ||
| if url.startswith("https://github.com/"): | ||
| return True | ||
| logging_error(_("Windows installer URL must be from github.com: %s"), url) | ||
| logging_error(_("Installer URL must be from github.com: %s"), url) | ||
| return False |
There was a problem hiding this comment.
Using startswith(\"https://github.com/\") is a brittle URL validation approach (e.g., it won’t handle case normalization, leading/trailing whitespace, or other valid GitHub URL forms consistently). A more robust and security-oriented approach is to parse the URL (e.g., via urllib.parse.urlparse) and strictly validate scheme == \"https\" and hostname == \"github.com\" (optionally also constraining the path prefix to /OWNER/REPO/releases/download/).
☂️ Python Coverage
Overall Coverage
New FilesNo new covered files... Modified Files
|
Test Results 2 files 2 suites 28m 37s ⏱️ Results for commit 29d5a7e. ♻️ This comment has been updated with latest results. |
Sure , do you want me to test something specific locally or should i go ahead and try to fix the errors? |
|
It would be great if you could test that it works. |
|
@amilcarlucas , I hardcoded my local version to
logs: |
|
Just a small thing I noticed that the link(full changelog etc) inside the update banner is not clickable. I dont know if its behavorial or not, i saw it and just wanted to point it out. |
acc46af to
7be6148
Compare
7be6148 to
29d5a7e
Compare
|
@OmkarSarkar204 Thanks for testing and for the feedback. The URLs are not clickable yet. A webbrowser opens in the backgound with the same information. The user can use that if he wants to click on something. |
Got it. Thanks! |

@OmkarSarkar204 can you take a look at this?