cyclo-manager 0.2.0.dev1__tar.gz → 0.2.0.dev3__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.dev3}/PKG-INFO +1 -1
  2. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_host_agent/routers/repos.py +13 -17
  3. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager.egg-info/PKG-INFO +1 -1
  4. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager_cli/cli.py +65 -19
  5. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/pyproject.toml +1 -1
  6. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/setup.cfg +1 -1
  7. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/README.md +0 -0
  8. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_host_agent/__init__.py +0 -0
  9. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_host_agent/main.py +0 -0
  10. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_host_agent/models.py +0 -0
  11. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_host_agent/routers/__init__.py +0 -0
  12. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_host_agent/routers/update.py +0 -0
  13. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager.egg-info/SOURCES.txt +0 -0
  14. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager.egg-info/dependency_links.txt +0 -0
  15. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager.egg-info/entry_points.txt +0 -0
  16. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager.egg-info/requires.txt +0 -0
  17. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager.egg-info/top_level.txt +0 -0
  18. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager_cli/__init__.py +0 -0
  19. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/cyclo_manager_cli/config/config.yml +0 -0
  20. {cyclo_manager-0.2.0.dev1 → cyclo_manager-0.2.0.dev3}/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.dev3
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
@@ -22,7 +22,6 @@ import asyncio
22
22
  import json
23
23
  import os
24
24
  import re
25
- import subprocess
26
25
  import urllib.request
27
26
  from pathlib import Path
28
27
  from typing import Optional
@@ -87,23 +86,20 @@ def _fmt_cmd(cmd: str, output: str) -> str:
87
86
 
88
87
  # ── git helpers ────────────────────────────────────────────────────────────────
89
88
 
90
- def _git_sync(args: list[str], cwd: Optional[Path] = None) -> tuple[int, str, str]:
91
- try:
92
- result = subprocess.run(
93
- ['git', *args],
94
- cwd=str(cwd) if cwd else None,
95
- capture_output=True,
96
- timeout=GIT_TIMEOUT,
97
- )
98
- return result.returncode, result.stdout.decode(), result.stderr.decode()
99
- except subprocess.TimeoutExpired:
100
- return 1, '', 'git timed out'
101
- except Exception as e:
102
- return 1, '', str(e)
103
-
104
-
105
89
  async def _git(args: list[str], cwd: Optional[Path] = None) -> tuple[int, str, str]:
106
- return await asyncio.to_thread(_git_sync, args, cwd)
90
+ proc = await asyncio.create_subprocess_exec(
91
+ 'git', *args,
92
+ cwd=str(cwd) if cwd else None,
93
+ stdout=asyncio.subprocess.PIPE,
94
+ stderr=asyncio.subprocess.PIPE,
95
+ )
96
+ try:
97
+ stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=GIT_TIMEOUT)
98
+ except asyncio.TimeoutError:
99
+ proc.kill()
100
+ await proc.wait()
101
+ raise
102
+ return proc.returncode, stdout.decode(), stderr.decode()
107
103
 
108
104
 
109
105
  async def _repo_info(repo_path: Path) -> RepoInfo:
@@ -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.dev3
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
@@ -33,6 +33,7 @@ import sys
33
33
  PYPI_PACKAGE = 'cyclo-manager'
34
34
  HOST_AGENT_SERVICE = 'cyclo_host_agent'
35
35
  HOST_AGENT_SOCKET = '/var/run/robotis/agent_sockets/host/host_agent.sock'
36
+ HOST_AGENT_SOCKET_DIR = '/var/run/robotis/agent_sockets/host'
36
37
 
37
38
  # `cyclo_manager up` starts these immediately.
38
39
  COMPOSE_SERVICES_UP = ('cyclo_manager', 'ui')
@@ -53,24 +54,67 @@ def _packaged_config_path() -> Path:
53
54
  return _config_dir() / 'config.yml'
54
55
 
55
56
 
56
- def _check_host_agent() -> bool:
57
- """Return True if the cyclo_host_agent service is already active."""
58
- result = subprocess.run(
59
- ['systemctl', 'is-active', f'{HOST_AGENT_SERVICE}.service'],
60
- capture_output=True,
61
- text=True,
62
- )
63
- return result.returncode == 0
57
+ def _resolve_service_user() -> tuple[str, Path] | None:
58
+ """
59
+ Return (username, home) for the host agent systemd service.
64
60
 
61
+ Uses SUDO_USER when cyclo_manager up is invoked via sudo so the agent runs
62
+ as the login user (required for git repo scans under ~/...).
63
+ """
64
+ sudo_user = os.environ.get('SUDO_USER')
65
+ if sudo_user:
66
+ return sudo_user, Path(f'/home/{sudo_user}')
67
+ user = os.environ.get('USER') or os.getlogin()
68
+ if user == 'root':
69
+ print(
70
+ 'Error: cyclo_manager up must run as a normal user (e.g. robotis), not root.',
71
+ file=sys.stderr,
72
+ )
73
+ return None
74
+ return user, Path.home()
65
75
 
66
76
 
67
- def _create_host_agent() -> int:
77
+ def _setup_socket_dir(user: str) -> bool:
68
78
  """
69
- Write cyclo_host_agent systemd service unit file and enable it.
79
+ Ensure the host agent UDS directory exists and is owned by the service user.
70
80
 
71
- Runs as a persistent service (Restart=always); systemd starts it at boot
72
- and restarts it automatically if it exits unexpectedly.
81
+ Only host/ is chowned so other agent sockets under agent_sockets/ are untouched.
82
+ Removes a stale socket file (e.g. left by a previous root-owned service).
73
83
  """
84
+ try:
85
+ subprocess.run(
86
+ ['sudo', 'mkdir', '-p', HOST_AGENT_SOCKET_DIR],
87
+ check=True,
88
+ capture_output=True,
89
+ )
90
+ subprocess.run(
91
+ ['sudo', 'chown', f'{user}:{user}', HOST_AGENT_SOCKET_DIR],
92
+ check=True,
93
+ capture_output=True,
94
+ )
95
+ subprocess.run(
96
+ ['sudo', 'rm', '-f', HOST_AGENT_SOCKET],
97
+ check=True,
98
+ capture_output=True,
99
+ )
100
+ return True
101
+ except subprocess.CalledProcessError as e:
102
+ print(f'Failed to set up host agent socket directory: {e}', file=sys.stderr)
103
+ return False
104
+
105
+
106
+ def _ensure_host_agent() -> int:
107
+ """
108
+ Install or refresh cyclo_host_agent: systemd unit, socket dir, and service start.
109
+
110
+ Idempotent — safe to run on every `cyclo_manager up` (fixes upgraded units and
111
+ socket ownership after switching away from a root-owned service).
112
+ """
113
+ account = _resolve_service_user()
114
+ if account is None:
115
+ return 1
116
+
117
+ user, user_home = account
74
118
  agent_exe = shutil.which('cyclo_host_agent')
75
119
  if not agent_exe:
76
120
  print(
@@ -80,8 +124,9 @@ def _create_host_agent() -> int:
80
124
  )
81
125
  return 1
82
126
 
83
- current_user = os.environ.get('SUDO_USER') or os.environ.get('USER') or os.getlogin()
84
- user_home = Path(f'/home/{current_user}') if current_user else Path.home()
127
+ if not _setup_socket_dir(user):
128
+ return 1
129
+
85
130
  service_content = f"""\
86
131
  [Unit]
87
132
  Description=Cyclo Host Agent
@@ -89,7 +134,8 @@ After=network.target
89
134
 
90
135
  [Service]
91
136
  Type=simple
92
- User={current_user}
137
+ User={user}
138
+ Group={user}
93
139
  ExecStart={agent_exe}
94
140
  Environment=HOME={user_home}
95
141
  Restart=always
@@ -110,7 +156,8 @@ WantedBy=multi-user.target
110
156
  check=True,
111
157
  )
112
158
  subprocess.run(['sudo', 'systemctl', 'daemon-reload'], check=True)
113
- subprocess.run(['sudo', 'systemctl', 'enable', '--now', f'{HOST_AGENT_SERVICE}.service'], check=True)
159
+ subprocess.run(['sudo', 'systemctl', 'enable', f'{HOST_AGENT_SERVICE}.service'], check=True)
160
+ subprocess.run(['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'], check=True)
114
161
  print('Host agent service installed and started.')
115
162
  return 0
116
163
  except subprocess.CalledProcessError as e:
@@ -123,9 +170,8 @@ WantedBy=multi-user.target
123
170
 
124
171
  def cmd_up(args: argparse.Namespace) -> int:
125
172
  """Start API + UI; create zenoh and noVNC containers without starting them."""
126
- if not _check_host_agent():
127
- if _create_host_agent() != 0:
128
- print('Warning: Failed to install host agent service.')
173
+ if _ensure_host_agent() != 0:
174
+ print('Warning: Failed to install host agent service.', file=sys.stderr)
129
175
 
130
176
  config_path = _packaged_config_path()
131
177
  if not config_path.is_file():
@@ -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.dev3"
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 = .