cyclo-manager 0.2.0.dev1__tar.gz → 0.2.0.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (20) hide show
  1. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/PKG-INFO +1 -1
  2. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_host_agent/routers/repos.py +14 -13
  3. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_host_agent/routers/update.py +18 -56
  4. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager.egg-info/PKG-INFO +1 -1
  5. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager_cli/cli.py +15 -1
  6. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/pyproject.toml +1 -1
  7. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/setup.cfg +1 -1
  8. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/README.md +0 -0
  9. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_host_agent/__init__.py +0 -0
  10. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_host_agent/main.py +0 -0
  11. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_host_agent/models.py +0 -0
  12. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_host_agent/routers/__init__.py +0 -0
  13. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager.egg-info/SOURCES.txt +0 -0
  14. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager.egg-info/dependency_links.txt +0 -0
  15. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager.egg-info/entry_points.txt +0 -0
  16. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager.egg-info/requires.txt +0 -0
  17. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager.egg-info/top_level.txt +0 -0
  18. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager_cli/__init__.py +0 -0
  19. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager_cli/config/config.yml +0 -0
  20. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev2}/cyclo_manager_cli/docker/docker-compose.yml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 0.2.0.dev1
3
+ Version: 0.2.0.dev2
4
4
  Summary: cyclo_manager CLI: pip-installable launcher for cyclo_manager server and UI containers. Run 'cyclo_manager up' to start Docker stack.
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -283,24 +283,25 @@ async def _update_reset(repo_path: Path, preserve_files: list[str]) -> UpdateRes
283
283
 
284
284
  # ── container helper ───────────────────────────────────────────────────────────
285
285
 
286
- async def _run_container_sh(repo_path: Path, action: str) -> tuple[bool, str]:
286
+ def _run_container_sh_sync(repo_path: Path, action: str) -> tuple[bool, str]:
287
287
  script = repo_path / 'docker' / 'container.sh'
288
288
  if not script.exists():
289
289
  return False, f'container.sh not found at {script}'
290
- proc = await asyncio.create_subprocess_exec(
291
- 'bash', str(script), action,
292
- cwd=str(repo_path / 'docker'),
293
- stdin=asyncio.subprocess.PIPE,
294
- stdout=asyncio.subprocess.PIPE,
295
- stderr=asyncio.subprocess.PIPE,
296
- )
297
290
  try:
298
- stdout, stderr = await asyncio.wait_for(proc.communicate(input=b'y\n'), timeout=300.0)
299
- except asyncio.TimeoutError:
300
- proc.kill()
301
- await proc.wait()
291
+ result = subprocess.run(
292
+ ['bash', str(script), action],
293
+ cwd=str(repo_path / 'docker'),
294
+ input=b'y\n',
295
+ capture_output=True,
296
+ timeout=300.0,
297
+ )
298
+ return result.returncode == 0, (result.stdout.decode() + result.stderr.decode()).strip()
299
+ except subprocess.TimeoutExpired:
302
300
  return False, 'Timeout waiting for container.sh'
303
- return proc.returncode == 0, (stdout.decode() + stderr.decode()).strip()
301
+
302
+
303
+ async def _run_container_sh(repo_path: Path, action: str) -> tuple[bool, str]:
304
+ return await asyncio.to_thread(_run_container_sh_sync, repo_path, action)
304
305
 
305
306
 
306
307
  # ── endpoints ──────────────────────────────────────────────────────────────────
@@ -42,16 +42,15 @@ def _fmt(cmd: str, out: str) -> str:
42
42
  return f'$ {cmd}\n{out.strip()}' if out.strip() else f'$ {cmd}'
43
43
 
44
44
 
45
- def _restart_self() -> None:
46
- """Restart the host agent after a short delay so new binary takes effect."""
47
- time.sleep(2)
45
+
46
+ def _run_cmd(args: list[str], timeout: float) -> tuple[int, str]:
48
47
  try:
49
- subprocess.run(
50
- ['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'],
51
- check=True,
52
- )
48
+ result = subprocess.run(args, capture_output=True, timeout=timeout)
49
+ return result.returncode, (result.stdout.decode() + result.stderr.decode()).strip()
50
+ except subprocess.TimeoutExpired:
51
+ return 1, 'timed out'
53
52
  except Exception as e:
54
- logger.error('Failed to restart host agent: %s', e)
53
+ return 1, str(e)
55
54
 
56
55
 
57
56
  async def _run_install_and_up(cyclo_exe: str, pip_exe: str) -> None:
@@ -60,50 +59,24 @@ async def _run_install_and_up(cyclo_exe: str, pip_exe: str) -> None:
60
59
 
61
60
  # pip install -U
62
61
  _update_status['phase'] = 'installing'
63
- try:
64
- proc = await asyncio.create_subprocess_exec(
65
- pip_exe, 'install', '-U', PYPI_PACKAGE,
66
- stdout=asyncio.subprocess.PIPE,
67
- stderr=asyncio.subprocess.PIPE,
68
- )
69
- stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=120)
70
- out = (stdout.decode() + stderr.decode()).strip()
71
- _update_status['install_output'] = _fmt(f'pip install -U {PYPI_PACKAGE}', out)
72
- if proc.returncode != 0:
73
- _update_status['phase'] = 'error'
74
- _update_status['error'] = f'pip install failed (exit {proc.returncode})'
75
- return
76
- except asyncio.TimeoutError:
62
+ rc, out = await asyncio.to_thread(_run_cmd, [pip_exe, 'install', '-U', PYPI_PACKAGE], 120)
63
+ _update_status['install_output'] = _fmt(f'pip install -U {PYPI_PACKAGE}', out)
64
+ if rc != 0:
77
65
  _update_status['phase'] = 'error'
78
- _update_status['error'] = 'pip install timed out'
66
+ _update_status['error'] = f'pip install failed (exit {rc})'
79
67
  return
80
68
 
81
69
  # cyclo up
82
70
  _update_status['phase'] = 'starting'
83
- try:
84
- proc = await asyncio.create_subprocess_exec(
85
- cyclo_exe, 'up',
86
- stdout=asyncio.subprocess.PIPE,
87
- stderr=asyncio.subprocess.PIPE,
88
- )
89
- stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=300)
90
- out = (stdout.decode() + stderr.decode()).strip()
91
- _update_status['up_output'] = _fmt('cyclo_manager up', out)
92
- if proc.returncode != 0:
93
- _update_status['phase'] = 'error'
94
- _update_status['error'] = f'cyclo_manager up failed (exit {proc.returncode})'
95
- return
96
- except asyncio.TimeoutError:
71
+ rc, out = await asyncio.to_thread(_run_cmd, [cyclo_exe, 'up'], 300)
72
+ _update_status['up_output'] = _fmt('cyclo_manager up', out)
73
+ if rc != 0:
97
74
  _update_status['phase'] = 'error'
98
- _update_status['error'] = 'cyclo_manager up timed out'
75
+ _update_status['error'] = f'cyclo_manager up failed (exit {rc})'
99
76
  return
100
77
 
101
78
  _update_status['phase'] = 'done'
102
79
 
103
- # Restart self so new binary takes effect
104
- loop = asyncio.get_event_loop()
105
- await loop.run_in_executor(None, _restart_self)
106
-
107
80
 
108
81
  @router.post('/update')
109
82
  async def start_update() -> dict:
@@ -127,20 +100,9 @@ async def start_update() -> dict:
127
100
 
128
101
  _update_status = {'phase': 'stopping', 'install_output': '', 'up_output': '', 'error': ''}
129
102
 
130
- # cyclo down (synchronous — server goes down here)
131
- down_output = ''
132
- try:
133
- proc = await asyncio.create_subprocess_exec(
134
- cyclo_exe, 'down',
135
- stdout=asyncio.subprocess.PIPE,
136
- stderr=asyncio.subprocess.PIPE,
137
- )
138
- stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=30)
139
- down_output = _fmt('cyclo_manager down', (stdout.decode() + stderr.decode()))
140
- except asyncio.TimeoutError:
141
- down_output = '$ cyclo_manager down\n(timed out)'
142
- except Exception as e:
143
- down_output = f'$ cyclo_manager down\n(error: {e})'
103
+ # cyclo down
104
+ rc, out = await asyncio.to_thread(_run_cmd, [cyclo_exe, 'down'], 30)
105
+ down_output = _fmt('cyclo_manager down', out)
144
106
 
145
107
  # Continue install + up in background
146
108
  asyncio.create_task(_run_install_and_up(cyclo_exe, pip_exe))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 0.2.0.dev1
3
+ Version: 0.2.0.dev2
4
4
  Summary: cyclo_manager CLI: pip-installable launcher for cyclo_manager server and UI containers. Run 'cyclo_manager up' to start Docker stack.
5
5
  License-Expression: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -123,7 +123,12 @@ WantedBy=multi-user.target
123
123
 
124
124
  def cmd_up(args: argparse.Namespace) -> int:
125
125
  """Start API + UI; create zenoh and noVNC containers without starting them."""
126
- if not _check_host_agent():
126
+ if _check_host_agent():
127
+ try:
128
+ subprocess.run(['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'], check=True)
129
+ except (subprocess.CalledProcessError, FileNotFoundError):
130
+ print('Warning: Failed to restart host agent service.', file=sys.stderr)
131
+ else:
127
132
  if _create_host_agent() != 0:
128
133
  print('Warning: Failed to install host agent service.')
129
134
 
@@ -232,6 +237,15 @@ def cmd_update(args: argparse.Namespace) -> int:
232
237
  except subprocess.CalledProcessError as e:
233
238
  return e.returncode
234
239
 
240
+ print('Restarting host agent...')
241
+ try:
242
+ subprocess.run(
243
+ ['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'],
244
+ check=True,
245
+ )
246
+ except (subprocess.CalledProcessError, FileNotFoundError):
247
+ print('Warning: Failed to restart host agent service.', file=sys.stderr)
248
+
235
249
  print('cyclo_manager update completed.')
236
250
  return 0
237
251
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cyclo-manager"
7
- version = "0.2.0.dev1"
7
+ version = "0.2.0.dev2"
8
8
  description = "cyclo_manager CLI: pip-installable launcher for cyclo_manager server and UI containers. Run 'cyclo_manager up' to start Docker stack."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = cyclo-manager
3
- version = 0.2.0dev
3
+ version = 0.2.0de2
4
4
 
5
5
  [egg_info]
6
6
  egg_base = .