Add Herdr update and live handoff menu actions
The Ctrl+A h command menu gains two host-scoped actions: a plain `herdr update` that streams updater output into the popup, and an experimental update-and-hot-restart flow that verifies a live server handoff and reconnects the originating Hub tab once the popup closes. Remote updates use the remote host's own proxy settings. A standalone helper reads literal proxy assignments from shell profiles and running Clash/Mihomo listeners without executing startup files; Windows reads process, user and machine proxy variables through PowerShell.
This commit is contained in:
@@ -8,6 +8,7 @@ import tempfile
|
||||
import time
|
||||
import unittest
|
||||
import uuid
|
||||
from unittest.mock import patch
|
||||
|
||||
from herdr_hub.cli import (
|
||||
BOOTSTRAP_WINDOW_NAME,
|
||||
@@ -15,11 +16,32 @@ from herdr_hub.cli import (
|
||||
HubError,
|
||||
TmuxHub,
|
||||
WINDOW_SHORT_LABEL_OPTION,
|
||||
build_parser,
|
||||
command_control,
|
||||
)
|
||||
|
||||
|
||||
@unittest.skipUnless(shutil.which("tmux"), "tmux is required")
|
||||
class TmuxIntegrationTests(unittest.TestCase):
|
||||
def test_verified_handoff_reconnects_only_originating_client(self) -> None:
|
||||
self.hub.ensure_server()
|
||||
original = self.hub.create_window(Endpoint.local("first"), str(self.fake_herdr))
|
||||
other = self.hub.create_window(Endpoint.local("second"), str(self.fake_herdr))
|
||||
self.hub.select_window(original.window_id)
|
||||
def pid(window):
|
||||
return self.hub.run(["display-message", "-p", "-t", window.window_id, "#{pane_pid}"]).stdout.strip()
|
||||
original_pid, other_pid = pid(original), pid(other)
|
||||
def close_popup(run):
|
||||
self.hub.select_window(other.window_id)
|
||||
run.__self__.reconnect_requested = True
|
||||
return 0
|
||||
with patch("herdr_hub.cli.make_hub", return_value=self.hub), patch(
|
||||
"herdr_hub.cli.resolve_herdr", return_value=str(self.fake_herdr)
|
||||
), patch("herdr_hub.cli.curses.wrapper", side_effect=close_popup):
|
||||
self.assertEqual(command_control(build_parser().parse_args(["control"])), 0)
|
||||
self.assertNotEqual(pid(original), original_pid)
|
||||
self.assertEqual(pid(other), other_pid)
|
||||
|
||||
def setUp(self) -> None:
|
||||
self.temporary_directory = tempfile.TemporaryDirectory()
|
||||
self.addCleanup(self.temporary_directory.cleanup)
|
||||
|
||||
Reference in New Issue
Block a user