Skip to content

feat: notify users when a newer LeLab is available on GitHub#38

Merged
nicolas-rabault merged 6 commits into
mainfrom
feat/github-update-notifier
Jun 16, 2026
Merged

feat: notify users when a newer LeLab is available on GitHub#38
nicolas-rabault merged 6 commits into
mainfrom
feat/github-update-notifier

Conversation

@nicolas-rabault

Copy link
Copy Markdown
Member

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.py

  • Reads the installed commit from pip's direct_url.json and derives owner/repo from the install URL.
  • Compares it to the repo HEAD via the GitHub API (5s timeout, 10-min in-memory cache). Any network/parse failure degrades silently to "no update".
  • Editable/local clones have no 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 — useUpdateCheck hook + UpdateNotice dialog

  • Fires once on app load; skipped on the hosted HF Space (isHostedSpace()) since that runtime can't be updated this way.
  • Popup offers: the copy-able pip command, an Update now button, a don't ask again checkbox, a Later button, and the close (X).
  • The opt-out persists the latest SHA in 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.json parsing, editable → no nag, up-to-date / update-available / GitHub-unreachable branches. Network mocked.
  • Full suite: 152 passed; ruff and eslint clean.
  • Manually verified the popup end-to-end with a seeded backend: renders correctly, copy button works, "don't ask again" persists across reload, and a newer SHA re-surfaces the popup.

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.
Copilot AI review requested due to automatic review settings June 16, 2026 10:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.py module: reads installed commit from pip's direct_url.json, compares against GitHub HEAD (with 10-min caching and 5s timeout), and provides a synchronous pip upgrade endpoint.
  • New useUpdateCheck hook and UpdateNotice dialog 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.

Comment thread lelab/update.py Outdated
Comment thread lelab/update.py
Comment thread lelab/server.py Outdated
- 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
nicolas-rabault force-pushed the feat/github-update-notifier branch from 7960958 to d8ab54f Compare June 16, 2026 11:32
- 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.
@nicolas-rabault
nicolas-rabault merged commit fa88de1 into main Jun 16, 2026
6 checks passed
@nicolas-rabault
nicolas-rabault deleted the feat/github-update-notifier branch June 16, 2026 11:47
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.

2 participants