Skip to content

feat(update): add installation-aware updates for Web and CLI - #751

Open
Autumn0716 wants to merge 8 commits into
HKUDS:devfrom
Autumn0716:feature/web-self-update
Open

feat(update): add installation-aware updates for Web and CLI#751
Autumn0716 wants to merge 8 commits into
HKUDS:devfrom
Autumn0716:feature/web-self-update

Conversation

@Autumn0716

@Autumn0716 Autumn0716 commented Aug 2, 2026

Copy link
Copy Markdown

Description

Add installation-aware stable updates across the CLI and Web UI.

  • Detect PyPI, full editable source, CLI-only editable source, Docker, and unsupported installations without guessing.
  • Add deeptutor update --check, managed PyPI upgrades, and safe source fast-forwards to stable release tags.
  • Surface update status and release notes in the Web version badge.
  • Allow administrators to update and restart managed PyPI or clean editable Web installs while preserving launcher arguments.
  • Keep Docker host-managed: containers only receive image update and recreation guidance.
  • Persist a single update job so progress and failures survive the launcher restart boundary.

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

  • None.

Module(s) Affected

  • agents
  • api
  • config
  • core
  • knowledge
  • logging
  • services
  • tools
  • utils
  • web (Frontend)
  • docs (Documentation)
  • scripts
  • tests
  • Other: cli, runtime, update

Checklist

  • I have read and followed the contribution guidelines.
  • My code follows the project's coding standards.
  • I have run the repository checks and fixed issues in all files changed by this PR.
  • I have added relevant unit, integration, and end-to-end tests.
  • I have updated the documentation.
  • My changes do not introduce any new security vulnerabilities.

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.
  • Managed Playwright functional E2E: 9 passed.
  • pre-commit on all 43 PR files: all hooks passed, including Ruff, Prettier, detect-secrets, Bandit, and MyPy.

Additional Notes

pre-commit run --all-files passes every hook except that the repository's Prettier hook rewrites 33 unchanged files already present on upstream/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

DeepTutor Web sidebar showing the blue update button

Update confirmation

DeepTutor Web update confirmation dialog

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.
Copilot AI review requested due to automatic review settings August 2, 2026 10:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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.update module (installation detection, release lookup, persisted jobs, and a detached worker for PyPI/source updates).
  • Adds /api/v1/system/update + /api/v1/system/update/job endpoints and a Web UI update badge/action flow (including functional Playwright E2E coverage).
  • Adds the deeptutor update CLI 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.

Comment thread deeptutor/update/worker.py Outdated
Comment thread deeptutor_cli/update_cmd.py
@Autumn0716
Autumn0716 force-pushed the feature/web-self-update branch 2 times, most recently from 31d53a9 to f6676b5 Compare August 2, 2026 11:25
@Autumn0716
Autumn0716 requested a review from Copilot August 2, 2026 11:28

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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.
@Autumn0716
Autumn0716 force-pushed the feature/web-self-update branch from f6676b5 to e3f073a Compare August 2, 2026 11:50
@Autumn0716

Copy link
Copy Markdown
Author

Addressed the three points from the latest Copilot review:

  • Bounded update-status checks to three retries (four total attempts), with functional E2E coverage proving requests stop.
  • Reused the shared response parser so fetchUpdateStatus preserves FastAPI JSON detail, with a focused API test.
  • Removed every managed Playwright home on normal exit, SIGINT, and SIGTERM; the integration test now runs correctly under both Bun and the repository Node test runner.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 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_update validates 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. Since installation (dependency) and coordinator.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.

@Autumn0716

Copy link
Copy Markdown
Author

Additional test-isolation hardening is now included in d86625de:

  • bun run test:node compiles into a unique directory under node_modules/.cache, outside Bun test discovery.
  • The alias loader receives that exact directory instead of assuming web/dist/node-tests.
  • Normal completion, child failure, SIGINT, and SIGTERM all clean the generated directory.
  • Verified the real sequence: bun run test:node passes 375 tests and leaves no compiled test directory; a following unfiltered bun test also passes exactly 375 tests with no duplicate execution.

@Autumn0716

Copy link
Copy Markdown
Author

Addressed the latest Copilot installation-consistency concern in fc1d0bbe.

  • A Web request whose coordinator reports PYPI now verifies that the request-scoped installation is also PYPI.
  • A mismatch fails closed with HTTP 409 before UpdateJobStore.create(); the regression test asserts that no job state is written.
  • Focused router tests: 13 passed.
  • Full Python suite: 3618 passed, 7 skipped.
  • Two-axis review: no findings.
  • PR-wide pre-commit checks: all passed.

@Autumn0716
Autumn0716 requested a review from Copilot August 2, 2026 12:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Autumn0716
Autumn0716 requested a review from Copilot August 2, 2026 12:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Autumn0716
Autumn0716 requested a review from Copilot August 2, 2026 12:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Autumn0716
Autumn0716 requested a review from Copilot August 3, 2026 03:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Autumn0716
Autumn0716 requested a lite review from Copilot August 4, 2026 12:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Autumn0716
Autumn0716 requested a lite review from Copilot August 8, 2026 12:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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