feat(update): add installation-aware updates for Web and CLI - #751
feat(update): add installation-aware updates for Web and CLI#751Autumn0716 wants to merge 8 commits into
Conversation
Detect PyPI, full-source, CLI-only, and container installations and query only official stable releases. Add a read-only CLI check with deterministic unit, integration, and process-level E2E coverage.
There was a problem hiding this comment.
🟡 Not ready to approve
The CLI update prompt naming and the web-managed restart path do not fully match the PR’s stated behavior (package naming for source_cli, and preserving launcher arguments on restart).
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This PR adds installation-aware update detection and execution across the DeepTutor CLI and Web UI, including a persisted update-job mechanism that can survive the launcher restart boundary and a Web “version badge” that surfaces update status + release links.
Changes:
- Introduces a new
deeptutor.updatemodule (installation detection, release lookup, persisted jobs, and a detached worker for PyPI/source updates). - Adds
/api/v1/system/update+/api/v1/system/update/jobendpoints and a Web UI update badge/action flow (including functional Playwright E2E coverage). - Adds the
deeptutor updateCLI command, plus documentation and dependency updates (packaging).
File summaries
| File | Description |
|---|---|
| web/tests/update-badge.test.ts | Unit tests for update badge presentation logic. |
| web/tests/update-api.test.ts | Unit tests for Web update API client helpers. |
| web/tests/e2e/update-badge.e2e.ts | Playwright functional E2E for update badge/action flows. |
| web/playwright.config.ts | Adds managed server mode + a new functional E2E project. |
| web/package.json | Adds test:e2e script for functional Playwright runs. |
| web/locales/zh/app.json | Adds Chinese UI strings for update states/actions. |
| web/locales/en/app.json | Adds English UI strings for update states/actions. |
| web/lib/update-badge.ts | Maps backend update payload → UI-friendly badge presentation. |
| web/lib/update-api.ts | Adds typed API client for update status + update jobs. |
| web/components/sidebar/VersionBadge.tsx | Displays update status, release link, and renders update action. |
| web/components/sidebar/UpdateAction.tsx | Implements admin-gated update-and-restart UX with polling/reconnect handling. |
| tests/update/test_worker.py | Tests detached worker behavior for PyPI + source update jobs. |
| tests/update/test_web_restart_integration.py | Integration test for launcher handoff + restart completion behavior. |
| tests/update/test_source_updater.py | Tests safe fast-forward source updater preflight/apply behavior. |
| tests/update/test_release_provider.py | Tests release lookup logic for PyPI + GitHub latest release endpoints. |
| tests/update/test_release_provider_integration.py | Integration test crossing a real HTTP boundary for release lookup. |
| tests/update/test_jobs.py | Tests job store invariants (single active job, persistence, failure handling). |
| tests/update/test_installation_integration.py | Integration test for detecting an editable full install. |
| tests/update/test_coordinator.py | Tests coordinator check output and installation classification rules. |
| tests/services/session/test_turn_runtime_subscribe.py | Adds coverage for “any live conversation running?” runtime signal. |
| tests/runtime/test_launcher.py | Tests launcher handoff and restart completion marking. |
| tests/e2e/test_update_check_cli.py | E2E tests running the real CLI process for update checks/flows. |
| tests/cli/test_update_cli.py | Unit tests for deeptutor update CLI behavior (check/schedule/source update). |
| tests/cli/test_docs_contract.py | Updates docs contract to include new update subcommand. |
| tests/api/test_system_router.py | Tests update endpoints (status, job, web update constraints, admin gating). |
| requirements/cli.txt | Adds packaging dependency for CLI installs. |
| README.md | Documents update/restart behavior and new CLI commands. |
| pyproject.toml | Adds packaging to core dependencies. |
| packaging/deeptutor-cli/pyproject.toml | Adds packaging to CLI-only distribution deps. |
| deeptutor/update/worker.py | Detached worker: fixed pip upgrade + optional managed restart. |
| deeptutor/update/source.py | Safe source fast-forward updater with strict preflight invariants. |
| deeptutor/update/jobs.py | Durable job store + single active slot + detached worker launcher. |
| deeptutor/update/init.py | Installation detection, release provider, and coordinator APIs. |
| deeptutor/services/session/turn_runtime.py | Adds has_live_executions() for disruption checks. |
| deeptutor/runtime/launcher.py | Adds update job handoff + restart completion marking hooks. |
| deeptutor/api/routers/system.py | Adds update status/job endpoints + web update request endpoint. |
| deeptutor_cli/update_cmd.py | Implements deeptutor update / --check CLI command. |
| deeptutor_cli/README.md | Documents update behavior in CLI README (zh). |
| deeptutor_cli/main.py | Registers the new update command with the CLI app. |
Review details
- Files reviewed: 39/39 changed files
- Comments generated: 2
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
31d53a9 to
f6676b5
Compare
There was a problem hiding this comment.
🟡 Not ready to approve
The web update UI/client currently includes unbounded retry behavior and inconsistent error detail propagation that should be tightened before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (3)
web/components/sidebar/VersionBadge.tsx:45
- The update check retry loop re-schedules itself every 750ms on any fetch failure with no deadline/backoff, which can spam the backend (and keep the UI in a tight retry) during prolonged outages.
if (!controller.signal.aborted) {
setCheck(null);
setUpdate({ kind: "failed" });
retryTimer = setTimeout(checkForUpdate, UPDATE_CHECK_RETRY_MS);
}
web/lib/update-api.ts:49
- fetchUpdateStatus throws a generic status-only error and ignores FastAPI's JSON {detail} payload, even though responseError already extracts it. This loses actionable diagnostics (and creates inconsistent error handling vs requestWebUpdate/fetchUpdateJob).
web/playwright.config.ts:17 - When PW_MANAGED_SERVERS=1, the config creates a temp E2E_HOME directory but never cleans it up, which can leave many deeptutor-e2e-* folders behind across repeated local runs/CI retries.
- Files reviewed: 39/39 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
Persist a single trusted update job, execute a fixed deeptutor upgrade from a detached worker after the CLI exits, and retain terminal status without restarting the app. Cover confirmation, cancellation, duplicate jobs, failures, tamper rejection, and the real process boundary.
f6676b5 to
e3f073a
Compare
|
Addressed the three points from the latest Copilot review:
Validation: Python 3617 passed / 7 skipped; Bun source tests 374 passed; Node-runner tests 374 passed; managed E2E 9 passed; typecheck, lint, i18n parity, and all PR pre-commit hooks passed. |
There was a problem hiding this comment.
🟡 Not ready to approve
The Web update request endpoint should explicitly validate PYPI installation consistency (mirroring the existing SOURCE_WEB validation) to prevent scheduling the wrong update path if installation state differs between dependency resolution and the coordinator check.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (1)
deeptutor/api/routers/system.py:261
request_web_updatevalidates that a SOURCE_WEB install hasn’t changed between dependency resolution and the coordinator check, but it doesn’t do the equivalent validation for PYPI installs. Sinceinstallation(dependency) andcoordinator.check()both compute installation state independently, a mismatch could schedule the wrong job type if the environment changes mid-request (or if DI overrides are inconsistent). Adding an explicit PYPI-mode check keeps behavior symmetric and aligns with the safety constraints.
elif result.install_mode is not InstallMode.PYPI:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail="This installation cannot be updated from the Web app.",
)
- Files reviewed: 40/40 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
|
Additional test-isolation hardening is now included in
|
|
Addressed the latest Copilot installation-consistency concern in
|
Description
Add installation-aware stable updates across the CLI and Web UI.
deeptutor update --check, managed PyPI upgrades, and safe source fast-forwards to stable release tags.Safety constraints include backend-generated command arguments with no shell, refusal while conversations are active, and source-update rejection for dirty worktrees, detached HEADs, diverged branches, or non-fast-forward release tags. Source updates never stash, reset, rebase, or merge user work. The Web request revalidates the installation mode immediately before job creation; conflicting observations fail closed with HTTP 409.
Related Issues
Module(s) Affected
agentsapiconfigcoreknowledgeloggingservicestoolsutilsweb(Frontend)docs(Documentation)scriptstestscli,runtime,updateChecklist
Validation
uv run pytest -q: 3618 passed, 7 skipped.bun test: 375 passed.bun run test:node: 375 passed.bun x tsc --noEmit: passed.bun run lint: 0 errors (56 existing warnings).bun run i18n:parity: passed.pre-commiton all 43 PR files: all hooks passed, including Ruff, Prettier, detect-secrets, Bandit, and MyPy.Additional Notes
pre-commit run --all-filespasses every hook except that the repository's Prettier hook rewrites 33 unchanged files already present onupstream/dev. Those unrelated formatting changes are intentionally excluded; the same Prettier hook passes for every file in this PR.Source-update failures attempt to restore the application process, but do not promise a full Git or dependency rollback. Docker self-update remains out of scope because the container root filesystem may be read-only and image replacement belongs to the host.
UI Preview
The available-update state uses a compact blue download icon so it remains visually distinct from the Docs and GitHub links.
Update available
Update confirmation