Skip to content

refactor(updater): simplify source upgrade handoff#571

Merged
stephamie7 merged 12 commits into
devfrom
refactor/simple-upgrade-handoff
Jul 21, 2026
Merged

refactor(updater): simplify source upgrade handoff#571
stephamie7 merged 12 commits into
devfrom
refactor/simple-upgrade-handoff

Conversation

@xiami762

@xiami762 xiami762 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR simplifies source upgrades around a detached handoff: the active process prepares and backs up a verified release, while the handoff exclusively stops services, replaces source, installs the new runtime, and restores the previous service state.

It also preserves compatibility with legacy handoff protocols and shares the core source-installation path with flocks doctor.

Key Changes

  • Upgrade preparation (flocks/updater/updater.py)
    • Download, verify, and extract the source and Pro bundle before any service mutation.
    • Back up the current source in the active process; abort without stopping services when backup fails.
    • Capture ServiceConfig, daemon PID, and was_running once, then pass them to the detached handoff.
    • Remove source replacement, dependency installation, frontend build, and automatic rollback from the active process.
  • Detached handoff (flocks/updater/restart_handoff.py)
    • Wait for the parent process, stop managed and legacy services, and wait for control endpoints, PIDs, and configured ports to be released.
    • Overlay the verified source while preserving the virtual environment and runtime/configuration directories.
    • Run the shared core installer, then synchronously restore the service only when it was running before the upgrade.
    • Persist the failed stage, process output, backup path, and retained staging directory after post-replacement failures; no automatic rollback is attempted.
  • Shared installation and repair
    • Add install_or_repair_source() for uv sync --no-python-downloads, Pro installation, WebUI dependency installation/build, runtime validation, CLI refresh, and version markers.
    • Use a 300-second dependency-sync timeout on every platform, retry configured mirrors through the default source, and retain timeout stdout/stderr.
    • Make flocks doctor call the same core installer; Windows uses a helper so the current CLI process releases executable locks first.
  • Unified update entry points
    • Route WebUI/API source upgrades, flocks update, and Pro bundles containing core source through the same handoff.
    • Keep Pro-only operations and downgrade restart-only behavior on their existing non-source-replacement paths.
    • Remove the source-upgrade page, pause/resume control path, rollback handoff/recovery, and old dist-preservation behavior.
  • Backward compatibility and Windows hardening
    • Accept legacy handoff arguments and migrate split-service serve restarts to the current managed start command.
    • Recover custom backend/WebUI endpoints from legacy upgrade-state.json when old handoffs lost the WebUI PID record and fell back to port 5173.
    • Decode Windows subprocess output independently of the active console code page, avoiding GBK UnicodeDecodeError failures.

Impact Scope

  • User-visible behavior: Source upgrades have a single stop/install/start sequence. Services that were running return on the same host, port, legacy-server configuration, and browser/build settings; stopped services remain stopped. Upgrade progress no longer uses the temporary upgrade-page or pause/resume flow.
  • Compatibility / migration: No manual migration is required. Compatibility coverage includes legacy handoff payloads from earlier 2026.7 releases, current handoffs, custom ports, wildcard hosts, and Windows paths/output.
  • Public APIs / CLI: No public API or CLI flag is added. Internal updater/handoff arguments and progress sequencing change.
  • Configuration / environment: No new required configuration, environment variable, or secret. Existing mirror and npm registry settings continue to apply.
  • Dependencies: No new third-party dependency is introduced.
  • Performance / resources: Upgrade downtime includes dependency synchronization and WebUI build. Each uv sync attempt has a 300-second timeout. Source backups continue to consume storage according to the configured retention count.
  • Security / permissions: No authentication or authorization behavior changes. The handoff receives only local service configuration and local filesystem paths.

Business Logic to Review

  • The ordering invariant: verify/extract and back up first; spawn handoff; wait for the parent; stop and verify PID/port release; replace source; install; restore the captured service state.
  • Backup and replacement boundaries: backups exclude .venv, dependency directories, all dist output, logs, configuration, and runtime data; replacement preserves runtime/configuration directories but deliberately rebuilds webui/dist.
  • Failure semantics: backup failure leaves source and service state untouched; failures after source replacement retain diagnostics and the backup for manual recovery, keep the service stopped, and never invoke rollback.
  • Service configuration fidelity for 127.0.0.1, 0.0.0.0, explicit addresses, IPv6, custom ports, legacy backend endpoints, no_browser, and skip_frontend_build.
  • Legacy handoff detection and endpoint restoration: only legacy split-service serve payloads read upgrade-state.json; modern captured ServiceConfig remains authoritative.
  • Windows process-lock behavior, detached execution, byte-output normalization, and CLI wrapper refresh.
  • Separation between source-replacing Pro upgrades, Pro-only operations, pending receipts/license activation, and downgrade restart-only handoffs.

Why This Approach

The updater must replace the source and dependencies used by the process that initiated the update. A detached handoff provides the minimum safe boundary for releasing Python, CLI, daemon, and Windows file locks. Keeping backup and release preparation in the active process makes preflight failures non-disruptive; keeping stop, replacement, installation, and restart in one handoff makes the irreversible sequence explicit and testable. Reusing one core installer avoids separate updater and doctor dependency/build implementations without invoking the destructive full bootstrap installer.

Test Plan

  • Relevant automated regression suite:

    .venv/bin/pytest -q \
      tests/updater/test_updater.py \
      tests/updater/test_restart_handoff.py \
      tests/cli/test_service_manager.py \
      tests/cli/test_update_command.py \
      --deselect \
      tests/updater/test_restart_handoff.py::test_run_reports_pending_install_receipt_after_pro_bundle_tasks

    Result after the latest compatibility fixes: 235 passed, 1 deselected.

  • Broader updater, CLI lifecycle, API, and Pro coverage: 330 passed, 2 skipped.

  • Ruff checks passed for changed updater/handoff code.

  • git diff --check passed.

  • Real default-address daemon/backend restart: both PIDs changed, /api/health returned 200, and status was healthy.

  • Real source handoff: backup, replacement, shared install, restart, health check, and temporary-state cleanup completed.

  • Custom localhost ports were preserved across handoff.

  • Explicit interface and wildcard-host scenarios preserved their frontend and legacy backend endpoints.

  • Windows 2026.7.15 upgrade completed dependency sync, WebUI build, daemon restart, backend restart, and health recovery.

  • Legacy handoff regression verifies that a WebUI endpoint such as port 8888 is restored instead of falling back to 5173.

Compatibility, Migration & Rollback

  • Existing installations require no configuration or data migration.
  • Legacy handoff payloads are normalized before the current restart command is constructed.
  • The install directory layout remains unchanged; .venv, dependency caches, configuration, logs, and runtime data remain in place according to the existing preservation rules.
  • Automatic rollback is intentionally removed. A successful pre-update source backup is required before handoff; after source replacement, failures retain the backup, staging directory, failed stage, stdout, and stderr for manual recovery.
  • Services that were stopped before upgrade remain stopped. Services that were running are restarted only after installation and runtime validation succeed.

@xiami762
xiami762 force-pushed the refactor/simple-upgrade-handoff branch from 0c43e43 to 517059f Compare July 20, 2026 10:56
@stephamie7
stephamie7 merged commit 5862929 into dev Jul 21, 2026
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