cyclo-manager 1.0.0.dev0__tar.gz → 1.0.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-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/PKG-INFO +8 -6
  2. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/README.md +7 -5
  3. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_host_agent/routers/repos.py +5 -2
  4. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager.egg-info/PKG-INFO +8 -6
  5. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager_cli/__init__.py +1 -1
  6. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager_cli/cli.py +81 -20
  7. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager_cli/docker/docker-compose.yml +2 -2
  8. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/pyproject.toml +1 -1
  9. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_host_agent/__init__.py +0 -0
  10. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_host_agent/main.py +0 -0
  11. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_host_agent/models.py +0 -0
  12. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_host_agent/routers/__init__.py +0 -0
  13. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_host_agent/routers/update.py +0 -0
  14. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager.egg-info/SOURCES.txt +0 -0
  15. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager.egg-info/dependency_links.txt +0 -0
  16. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager.egg-info/entry_points.txt +0 -0
  17. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager.egg-info/requires.txt +0 -0
  18. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager.egg-info/top_level.txt +0 -0
  19. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/cyclo_manager_cli/config/config.yml +0 -0
  20. {cyclo_manager-1.0.0.dev0 → cyclo_manager-1.0.0.dev2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 1.0.0.dev0
3
+ Version: 1.0.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
@@ -52,9 +52,9 @@ For the full monorepo (API source, UI source, dev compose), see the [repository
52
52
 
53
53
  - **Docker** with **Compose v2** (`docker compose` — the legacy `docker-compose` binary alone is not enough)
54
54
  - **Python 3.10+**
55
- - **`sudo`** — required to install the host agent systemd unit, socket directory permissions, and sudoers rules
55
+ - **`sudo`** — required on normal-user hosts to install the host agent systemd unit, socket directory permissions, and sudoers rules
56
56
  - For **`cyclo_manager update`:** **`pip`** or **`pip3`** on `PATH`
57
- - **Do not run as root** — the host agent runs as the invoking user (or `SUDO_USER` when using `sudo cyclo_manager up`). Running as root is rejected.
57
+ - **Root shell handling** — root is rejected when a normal login user exists. On root-only devices, the host agent is installed as root.
58
58
  - **Agent sockets** on the host under `/var/run/robotis/agent_sockets/` (robot containers and host agent). The bundled Compose file bind-mounts this tree into the API container as `/agents/`.
59
59
 
60
60
  ---
@@ -120,7 +120,7 @@ The pip package installs a small **FastAPI** server that listens on a **Unix dom
120
120
  |------|------|
121
121
  | Socket | `/var/run/robotis/agent_sockets/host/host_agent.sock` |
122
122
  | systemd unit | `cyclo_host_agent.service` |
123
- | sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts) |
123
+ | sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts; skipped on root-only devices) |
124
124
 
125
125
  The API container reaches it at `/agents/host/host_agent.sock` (see bundled `config.yml`).
126
126
 
@@ -131,6 +131,8 @@ The API container reaches it at `/agents/host/host_agent.sock` (see bundled `con
131
131
 
132
132
  `cyclo_manager up` and `cyclo_manager update` both call `_ensure_host_agent()`, which is idempotent — safe to run after upgrades or user changes.
133
133
 
134
+ Repository scanning uses `CYCLO_HOST_AGENT_WORKSPACE` when set. Otherwise it uses the service user's home directory, except root-only devices with `/data/docker`, where `/data/docker` is used automatically.
135
+
134
136
  ---
135
137
 
136
138
  ## Compose services
@@ -139,8 +141,8 @@ Defined in [`cyclo_manager_cli/docker/docker-compose.yml`](cyclo_manager_cli/doc
139
141
 
140
142
  | Compose service | Container name | `cyclo_manager up` | Image (example) |
141
143
  |-----------------|----------------|--------------------|-----------------|
142
- | `cyclo_manager` | `cyclo_manager` | **Started** | `robotis/cyclo-manager:0.4.0` |
143
- | `ui` | `cyclo_manager_ui` | **Started** | `robotis/cyclo-manager-ui:0.4.0` |
144
+ | `cyclo_manager` | `cyclo_manager` | **Started** | `robotis/cyclo-manager:1.0.0` |
145
+ | `ui` | `cyclo_manager_ui` | **Started** | `robotis/cyclo-manager-ui:1.0.0` |
144
146
  | `rmw_zenoh` | `zenoh_daemon` | **Created only** | `robotis/zenoh-daemon:latest` |
145
147
  | `novnc-server` | `novnc-server` | **Created only** | `robotis/novnc-server:latest` |
146
148
 
@@ -38,9 +38,9 @@ For the full monorepo (API source, UI source, dev compose), see the [repository
38
38
 
39
39
  - **Docker** with **Compose v2** (`docker compose` — the legacy `docker-compose` binary alone is not enough)
40
40
  - **Python 3.10+**
41
- - **`sudo`** — required to install the host agent systemd unit, socket directory permissions, and sudoers rules
41
+ - **`sudo`** — required on normal-user hosts to install the host agent systemd unit, socket directory permissions, and sudoers rules
42
42
  - For **`cyclo_manager update`:** **`pip`** or **`pip3`** on `PATH`
43
- - **Do not run as root** — the host agent runs as the invoking user (or `SUDO_USER` when using `sudo cyclo_manager up`). Running as root is rejected.
43
+ - **Root shell handling** — root is rejected when a normal login user exists. On root-only devices, the host agent is installed as root.
44
44
  - **Agent sockets** on the host under `/var/run/robotis/agent_sockets/` (robot containers and host agent). The bundled Compose file bind-mounts this tree into the API container as `/agents/`.
45
45
 
46
46
  ---
@@ -106,7 +106,7 @@ The pip package installs a small **FastAPI** server that listens on a **Unix dom
106
106
  |------|------|
107
107
  | Socket | `/var/run/robotis/agent_sockets/host/host_agent.sock` |
108
108
  | systemd unit | `cyclo_host_agent.service` |
109
- | sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts) |
109
+ | sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts; skipped on root-only devices) |
110
110
 
111
111
  The API container reaches it at `/agents/host/host_agent.sock` (see bundled `config.yml`).
112
112
 
@@ -117,6 +117,8 @@ The API container reaches it at `/agents/host/host_agent.sock` (see bundled `con
117
117
 
118
118
  `cyclo_manager up` and `cyclo_manager update` both call `_ensure_host_agent()`, which is idempotent — safe to run after upgrades or user changes.
119
119
 
120
+ Repository scanning uses `CYCLO_HOST_AGENT_WORKSPACE` when set. Otherwise it uses the service user's home directory, except root-only devices with `/data/docker`, where `/data/docker` is used automatically.
121
+
120
122
  ---
121
123
 
122
124
  ## Compose services
@@ -125,8 +127,8 @@ Defined in [`cyclo_manager_cli/docker/docker-compose.yml`](cyclo_manager_cli/doc
125
127
 
126
128
  | Compose service | Container name | `cyclo_manager up` | Image (example) |
127
129
  |-----------------|----------------|--------------------|-----------------|
128
- | `cyclo_manager` | `cyclo_manager` | **Started** | `robotis/cyclo-manager:0.4.0` |
129
- | `ui` | `cyclo_manager_ui` | **Started** | `robotis/cyclo-manager-ui:0.4.0` |
130
+ | `cyclo_manager` | `cyclo_manager` | **Started** | `robotis/cyclo-manager:1.0.0` |
131
+ | `ui` | `cyclo_manager_ui` | **Started** | `robotis/cyclo-manager-ui:1.0.0` |
130
132
  | `rmw_zenoh` | `zenoh_daemon` | **Created only** | `robotis/zenoh-daemon:latest` |
131
133
  | `novnc-server` | `novnc-server` | **Created only** | `robotis/novnc-server:latest` |
132
134
 
@@ -43,14 +43,17 @@ router = APIRouter(prefix='/repos', tags=['repos'])
43
43
  GIT_TIMEOUT = 120.0
44
44
 
45
45
 
46
- def _resolve_home() -> Path:
46
+ def _resolve_workspace() -> Path:
47
+ configured_workspace = os.environ.get('CYCLO_HOST_AGENT_WORKSPACE')
48
+ if configured_workspace:
49
+ return Path(configured_workspace).expanduser()
47
50
  sudo_user = os.environ.get('SUDO_USER')
48
51
  if sudo_user:
49
52
  return Path(f'/home/{sudo_user}')
50
53
  return Path.home()
51
54
 
52
55
 
53
- WORKSPACE_PATH = _resolve_home()
56
+ WORKSPACE_PATH = _resolve_workspace()
54
57
  MANAGED_GITHUB_ORG = 'ROBOTIS-GIT'
55
58
  ALLOWED_UPDATE_BRANCHES = frozenset({'main', 'jazzy'})
56
59
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 1.0.0.dev0
3
+ Version: 1.0.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
@@ -52,9 +52,9 @@ For the full monorepo (API source, UI source, dev compose), see the [repository
52
52
 
53
53
  - **Docker** with **Compose v2** (`docker compose` — the legacy `docker-compose` binary alone is not enough)
54
54
  - **Python 3.10+**
55
- - **`sudo`** — required to install the host agent systemd unit, socket directory permissions, and sudoers rules
55
+ - **`sudo`** — required on normal-user hosts to install the host agent systemd unit, socket directory permissions, and sudoers rules
56
56
  - For **`cyclo_manager update`:** **`pip`** or **`pip3`** on `PATH`
57
- - **Do not run as root** — the host agent runs as the invoking user (or `SUDO_USER` when using `sudo cyclo_manager up`). Running as root is rejected.
57
+ - **Root shell handling** — root is rejected when a normal login user exists. On root-only devices, the host agent is installed as root.
58
58
  - **Agent sockets** on the host under `/var/run/robotis/agent_sockets/` (robot containers and host agent). The bundled Compose file bind-mounts this tree into the API container as `/agents/`.
59
59
 
60
60
  ---
@@ -120,7 +120,7 @@ The pip package installs a small **FastAPI** server that listens on a **Unix dom
120
120
  |------|------|
121
121
  | Socket | `/var/run/robotis/agent_sockets/host/host_agent.sock` |
122
122
  | systemd unit | `cyclo_host_agent.service` |
123
- | sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts) |
123
+ | sudoers | `/etc/sudoers.d/cyclo_manager` (`cp`, `udevadm` for container setup scripts; skipped on root-only devices) |
124
124
 
125
125
  The API container reaches it at `/agents/host/host_agent.sock` (see bundled `config.yml`).
126
126
 
@@ -131,6 +131,8 @@ The API container reaches it at `/agents/host/host_agent.sock` (see bundled `con
131
131
 
132
132
  `cyclo_manager up` and `cyclo_manager update` both call `_ensure_host_agent()`, which is idempotent — safe to run after upgrades or user changes.
133
133
 
134
+ Repository scanning uses `CYCLO_HOST_AGENT_WORKSPACE` when set. Otherwise it uses the service user's home directory, except root-only devices with `/data/docker`, where `/data/docker` is used automatically.
135
+
134
136
  ---
135
137
 
136
138
  ## Compose services
@@ -139,8 +141,8 @@ Defined in [`cyclo_manager_cli/docker/docker-compose.yml`](cyclo_manager_cli/doc
139
141
 
140
142
  | Compose service | Container name | `cyclo_manager up` | Image (example) |
141
143
  |-----------------|----------------|--------------------|-----------------|
142
- | `cyclo_manager` | `cyclo_manager` | **Started** | `robotis/cyclo-manager:0.4.0` |
143
- | `ui` | `cyclo_manager_ui` | **Started** | `robotis/cyclo-manager-ui:0.4.0` |
144
+ | `cyclo_manager` | `cyclo_manager` | **Started** | `robotis/cyclo-manager:1.0.0` |
145
+ | `ui` | `cyclo_manager_ui` | **Started** | `robotis/cyclo-manager-ui:1.0.0` |
144
146
  | `rmw_zenoh` | `zenoh_daemon` | **Created only** | `robotis/zenoh-daemon:latest` |
145
147
  | `novnc-server` | `novnc-server` | **Created only** | `robotis/novnc-server:latest` |
146
148
 
@@ -18,4 +18,4 @@
18
18
 
19
19
  """cyclo_manager CLI - pip-installable launcher for cyclo_manager server and UI containers."""
20
20
 
21
- __version__ = '0.4.0'
21
+ __version__ = '1.0.0'
@@ -26,6 +26,7 @@ Launches cyclo_manager server and UI via Docker.
26
26
  import argparse
27
27
  import os
28
28
  from pathlib import Path
29
+ import pwd
29
30
  import shutil
30
31
  import socket
31
32
  import subprocess
@@ -36,10 +37,11 @@ HOST_AGENT_SERVICE = 'cyclo_host_agent'
36
37
  HOST_AGENT_SOCKET = '/var/run/robotis/agent_sockets/host/host_agent.sock'
37
38
  HOST_AGENT_SOCKET_DIR = '/var/run/robotis/agent_sockets/host'
38
39
 
39
- # `cyclo_manager up` starts these immediately.
40
+ # `cyclo_manager up` starts these Containers immediately.
40
41
  COMPOSE_SERVICES_UP = ('cyclo_manager', 'ui')
41
42
  # These get `docker compose create` only (stopped); start from UI or `docker start`.
42
43
  COMPOSE_SERVICES_CREATE_ONLY = ('rmw_zenoh', 'novnc-server')
44
+ DISABLED_LOGIN_SHELLS = {'', '/bin/false', '/sbin/nologin', '/usr/sbin/nologin'}
43
45
 
44
46
 
45
47
  def _docker_dir() -> Path:
@@ -55,6 +57,47 @@ def _packaged_config_path() -> Path:
55
57
  return _config_dir() / 'config.yml'
56
58
 
57
59
 
60
+ def _sudo_cmd(args: list[str]) -> list[str]:
61
+ """Return args prefixed with sudo when the current process is not already root."""
62
+ if os.geteuid() == 0:
63
+ return args
64
+ return ['sudo', *args]
65
+
66
+
67
+ def _user_home(user: str) -> Path:
68
+ """Return a user's home directory from passwd, with a conservative fallback."""
69
+ try:
70
+ return Path(pwd.getpwnam(user).pw_dir)
71
+ except KeyError:
72
+ if user == 'root':
73
+ return Path('/root')
74
+ return Path(f'/home/{user}')
75
+
76
+
77
+ def _has_normal_login_user() -> bool:
78
+ """Return True when the host has a non-root login user."""
79
+ for account in pwd.getpwall():
80
+ if account.pw_name == 'root':
81
+ continue
82
+ if account.pw_uid < 1000 or account.pw_uid >= 65534:
83
+ continue
84
+ if account.pw_shell in DISABLED_LOGIN_SHELLS:
85
+ continue
86
+ return True
87
+ return False
88
+
89
+
90
+ def _host_agent_workspace(user: str, user_home: Path) -> Path:
91
+ """Return the workspace path the host agent should scan for git repositories."""
92
+ configured_workspace = os.environ.get('CYCLO_HOST_AGENT_WORKSPACE')
93
+ if configured_workspace:
94
+ return Path(configured_workspace).expanduser()
95
+ data_docker = Path('/data/docker')
96
+ if user == 'root' and data_docker.is_dir():
97
+ return data_docker
98
+ return user_home
99
+
100
+
58
101
  def _resolve_service_user() -> tuple[str, Path] | None:
59
102
  """
60
103
  Return (username, home) for the host agent systemd service.
@@ -64,9 +107,15 @@ def _resolve_service_user() -> tuple[str, Path] | None:
64
107
  """
65
108
  sudo_user = os.environ.get('SUDO_USER')
66
109
  if sudo_user:
67
- return sudo_user, Path(f'/home/{sudo_user}')
68
- user = os.environ.get('USER') or os.getlogin()
110
+ return sudo_user, _user_home(sudo_user)
111
+ user = os.environ.get('USER') or pwd.getpwuid(os.getuid()).pw_name
69
112
  if user == 'root':
113
+ if not _has_normal_login_user():
114
+ print(
115
+ 'Info: root-only device detected; cyclo_host_agent will run as root.',
116
+ file=sys.stderr,
117
+ )
118
+ return 'root', Path('/root')
70
119
  print(
71
120
  'Error: cyclo_manager up must run as a normal user (e.g. robotis), not root.',
72
121
  file=sys.stderr,
@@ -85,19 +134,19 @@ def _setup_socket_dir(user: str) -> bool:
85
134
  print(f' [1/3] Creating socket directory {HOST_AGENT_SOCKET_DIR}...')
86
135
  try:
87
136
  subprocess.run(
88
- ['sudo', 'mkdir', '-p', HOST_AGENT_SOCKET_DIR],
137
+ _sudo_cmd(['mkdir', '-p', HOST_AGENT_SOCKET_DIR]),
89
138
  check=True,
90
139
  capture_output=True,
91
140
  )
92
141
  print(f' [2/3] Setting ownership to {user}:{user}...')
93
142
  subprocess.run(
94
- ['sudo', 'chown', f'{user}:{user}', HOST_AGENT_SOCKET_DIR],
143
+ _sudo_cmd(['chown', f'{user}:{user}', HOST_AGENT_SOCKET_DIR]),
95
144
  check=True,
96
145
  capture_output=True,
97
146
  )
98
147
  print(' [3/3] Removing stale socket file (if any)...')
99
148
  subprocess.run(
100
- ['sudo', 'rm', '-f', HOST_AGENT_SOCKET],
149
+ _sudo_cmd(['rm', '-f', HOST_AGENT_SOCKET]),
101
150
  check=True,
102
151
  capture_output=True,
103
152
  )
@@ -123,13 +172,13 @@ def _install_sudoers(user: str) -> bool:
123
172
  sudoers_file = '/etc/sudoers.d/cyclo_manager'
124
173
  try:
125
174
  subprocess.run(
126
- ['sudo', 'tee', sudoers_file],
175
+ _sudo_cmd(['tee', sudoers_file]),
127
176
  input=sudoers_content.encode(),
128
177
  capture_output=True,
129
178
  check=True,
130
179
  )
131
180
  subprocess.run(
132
- ['sudo', 'chmod', '440', sudoers_file],
181
+ _sudo_cmd(['chmod', '440', sudoers_file]),
133
182
  check=True,
134
183
  capture_output=True,
135
184
  )
@@ -151,6 +200,7 @@ def _ensure_host_agent() -> int:
151
200
  return 1
152
201
 
153
202
  user, user_home = account
203
+ workspace_path = _host_agent_workspace(user, user_home)
154
204
  agent_exe = shutil.which('cyclo_host_agent')
155
205
  if not agent_exe:
156
206
  print(
@@ -163,7 +213,7 @@ def _ensure_host_agent() -> int:
163
213
  if not _setup_socket_dir(user):
164
214
  return 1
165
215
 
166
- if not _install_sudoers(user):
216
+ if user != 'root' and not _install_sudoers(user):
167
217
  return 1
168
218
 
169
219
  service_content = f"""\
@@ -179,6 +229,7 @@ RuntimeDirectory=robotis/agent_sockets/host
179
229
  RuntimeDirectoryMode=0755
180
230
  ExecStart={agent_exe}
181
231
  Environment=HOME={user_home}
232
+ Environment=CYCLO_HOST_AGENT_WORKSPACE={workspace_path}
182
233
  Restart=always
183
234
  RestartSec=3
184
235
 
@@ -192,22 +243,26 @@ WantedBy=multi-user.target
192
243
  try:
193
244
  print(f' Writing unit file to {service_file}...')
194
245
  subprocess.run(
195
- ['sudo', 'tee', service_file],
246
+ _sudo_cmd(['tee', service_file]),
196
247
  input=service_content.encode(),
197
248
  capture_output=True,
198
249
  check=True,
199
250
  )
200
251
  print(' Running systemctl daemon-reload...')
201
- subprocess.run(['sudo', 'systemctl', 'daemon-reload'], check=True, capture_output=True)
252
+ subprocess.run(
253
+ _sudo_cmd(['systemctl', 'daemon-reload']),
254
+ check=True,
255
+ capture_output=True,
256
+ )
202
257
  print(f' Enabling {HOST_AGENT_SERVICE}...')
203
258
  subprocess.run(
204
- ['sudo', 'systemctl', 'enable', f'{HOST_AGENT_SERVICE}.service'],
259
+ _sudo_cmd(['systemctl', 'enable', f'{HOST_AGENT_SERVICE}.service']),
205
260
  check=True,
206
261
  capture_output=True,
207
262
  )
208
263
  print(f' Starting {HOST_AGENT_SERVICE}...')
209
264
  subprocess.run(
210
- ['sudo', 'systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service'],
265
+ _sudo_cmd(['systemctl', 'restart', f'{HOST_AGENT_SERVICE}.service']),
211
266
  check=True,
212
267
  capture_output=True,
213
268
  )
@@ -217,7 +272,7 @@ WantedBy=multi-user.target
217
272
  print(f'Failed to install host agent service: {e}', file=sys.stderr)
218
273
  return 1
219
274
  except FileNotFoundError:
220
- print('sudo not found. Install the host agent service manually.', file=sys.stderr)
275
+ print('Required command not found. Install the host agent service manually.', file=sys.stderr)
221
276
  return 1
222
277
 
223
278
 
@@ -272,15 +327,21 @@ def _teardown_host_agent() -> None:
272
327
  """Stop and remove the host agent service, unit file, sudoers, and socket dir."""
273
328
  print(f'Tearing down {HOST_AGENT_SERVICE}...')
274
329
  steps = [
275
- (['sudo', 'systemctl', 'stop', f'{HOST_AGENT_SERVICE}.service'], 'stop service'),
276
- (['sudo', 'systemctl', 'disable', f'{HOST_AGENT_SERVICE}.service'], 'disable service'),
277
330
  (
278
- ['sudo', 'rm', '-f', f'/etc/systemd/system/{HOST_AGENT_SERVICE}.service'],
331
+ _sudo_cmd(['systemctl', 'stop', f'{HOST_AGENT_SERVICE}.service']),
332
+ 'stop service',
333
+ ),
334
+ (
335
+ _sudo_cmd(['systemctl', 'disable', f'{HOST_AGENT_SERVICE}.service']),
336
+ 'disable service',
337
+ ),
338
+ (
339
+ _sudo_cmd(['rm', '-f', f'/etc/systemd/system/{HOST_AGENT_SERVICE}.service']),
279
340
  'remove unit file',
280
341
  ),
281
- (['sudo', 'systemctl', 'daemon-reload'], 'daemon-reload'),
282
- (['sudo', 'rm', '-f', '/etc/sudoers.d/cyclo_manager'], 'remove sudoers'),
283
- (['sudo', 'rm', '-rf', HOST_AGENT_SOCKET_DIR], 'remove socket dir'),
342
+ (_sudo_cmd(['systemctl', 'daemon-reload']), 'daemon-reload'),
343
+ (_sudo_cmd(['rm', '-f', '/etc/sudoers.d/cyclo_manager']), 'remove sudoers'),
344
+ (_sudo_cmd(['rm', '-rf', HOST_AGENT_SOCKET_DIR]), 'remove socket dir'),
284
345
  ]
285
346
  for cmd, desc in steps:
286
347
  print(f' {desc}...')
@@ -11,7 +11,7 @@ services:
11
11
 
12
12
  cyclo_manager:
13
13
  container_name: cyclo_manager
14
- image: robotis/cyclo-manager:0.4.0
14
+ image: robotis/cyclo-manager:1.0.0
15
15
  restart: always
16
16
  network_mode: host
17
17
  volumes:
@@ -27,7 +27,7 @@ services:
27
27
 
28
28
  ui:
29
29
  container_name: cyclo_manager_ui
30
- image: robotis/cyclo-manager-ui:0.4.0
30
+ image: robotis/cyclo-manager-ui:1.0.0
31
31
  restart: always
32
32
  network_mode: host
33
33
  environment:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cyclo-manager"
7
- version = "1.0.0.dev0"
7
+ version = "1.0.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"