feat: notify users when a newer LeLab is available on GitHub#38
Merged
Conversation
LeLab installs from git, so "newer" means the default branch has moved past the installed commit. New `lelab/update.py` reads the installed commit from pip's `direct_url.json` and compares it to the repo HEAD via the GitHub API (cached, silent on failure). Editable/local clones have no commit_id and are deliberately excluded so developers aren't nagged. Exposes `GET /update-check` and a best-effort `POST /update` (runs the pip upgrade in-process; the user restarts afterwards). Frontend `useUpdateCheck` + `UpdateAvailableDialog` surface a popup with the copy-able upgrade command, an "Update now" button, and a "don't ask again" checkbox. The opt-out stores the latest SHA, so a brand-new version re-surfaces the popup automatically. Skipped entirely on the hosted HF Space.
There was a problem hiding this comment.
Pull request overview
This PR adds an in-app update notifier to LeLab. Since LeLab is installed from git (no tags/releases), the system compares the locally installed commit SHA against the repository's HEAD via the GitHub API. When a newer version is detected, a dialog popup offers the user a copy-able pip command, a one-click auto-update button, and a "don't ask again" opt-out that persists per-SHA.
Changes:
- New
lelab/update.pymodule: reads installed commit from pip'sdirect_url.json, compares against GitHub HEAD (with 10-min caching and 5s timeout), and provides a synchronous pip upgrade endpoint. - New
useUpdateCheckhook andUpdateNoticedialog in the frontend: fires once on load, skips on hosted Spaces, and surfaces a dismissible popup when behind. - Unit tests covering URL parsing, source detection, and all update-check branches.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
lelab/update.py |
Core update logic: source detection, GitHub API comparison, cache, and subprocess-based auto-update |
lelab/server.py |
Registers GET /update-check and POST /update endpoints |
frontend/src/hooks/useUpdateCheck.ts |
React hook that fetches update status and manages dismiss/localStorage state |
frontend/src/components/UpdateNotice.tsx |
Dialog component with copy command, update button, and "don't ask again" |
frontend/src/App.tsx |
Mounts UpdateNotice at app root alongside existing global components |
tests/test_update.py |
Unit tests for URL parsing, source detection, and update-check scenarios |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Display the exact command the Update button runs (derived from the same builder), so it uses `uv pip install` on uv-based/standard installs instead of a hardcoded pip line that wouldn't match. - Let the command box wrap (break-all) so the full command fits in the dialog. - Style the "don't ask again" checkbox to the same slate tone as its label so it's actually visible on the dark dialog.
Most users will click Update now, so the manual command is no longer the default focus. Tuck it behind an 'Or update manually' collapsible, keeping the copy button for those who want it.
nicolas-rabault
force-pushed
the
feat/github-update-notifier
branch
from
June 16, 2026 11:32
7960958 to
d8ab54f
Compare
- Move endpoints to /system/update-check and /system/update for consistency with the other system-management routes; update the frontend paths to match. - Suppress bandit B310 on the GitHub API call (URL is a fixed https constant, no user-controlled scheme) so pre-commit passes. - Document why /system/update uses a blocking subprocess.run instead of the streaming InstallManager pattern (one-shot fire-and-restart, no live logs).
The dialog auto-focused the first focusable element (the 'Or update manually' trigger), showing a stray focus ring. Prevent the open auto-focus so nothing is ring-highlighted on appear; keyboard tab-focus is unaffected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an in-app update notifier. On load, LeLab checks GitHub for a newer version and, if the install is behind, shows a popup with the upgrade command, a one-click Update now button, and a don't ask again opt-out.
LeLab installs from git (
pip install git+https://github.com/.../leLab.git), so "newer" means the default branch has moved past the installed commit — we don't rely on tags/releases (there are none yet).How
Backend — new
lelab/update.pydirect_url.jsonand derivesowner/repofrom the install URL.HEADvia the GitHub API (5s timeout, 10-min in-memory cache). Any network/parse failure degrades silently to "no update".commit_id→ excluded, so developers aren't nagged.GET /update-check→{ update_available, current_commit, latest_commit, commits_behind, compare_url, update_command, can_auto_update }.POST /update→ runs the pip upgrade in-process (best-effort; user restarts afterwards).Frontend —
useUpdateCheckhook +UpdateNoticedialogisHostedSpace()) since that runtime can't be updated this way.localStorage; the popup only re-appears when a different (newer) SHA is published — so a new version automatically clears the opt-out.Testing
tests/test_update.py(15 cases): URL parsing,direct_url.jsonparsing, editable → no nag, up-to-date / update-available / GitHub-unreachable branches. Network mocked.ruffandeslintclean.