cyclo-manager 0.2.0.dev3__tar.gz → 0.2.0.dev5__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.dev3 → cyclo_manager-0.2.0.dev5}/PKG-INFO +1 -1
  2. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_host_agent/routers/repos.py +24 -29
  3. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager.egg-info/PKG-INFO +1 -1
  4. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager_cli/cli.py +2 -8
  5. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/pyproject.toml +1 -1
  6. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/README.md +0 -0
  7. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_host_agent/__init__.py +0 -0
  8. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_host_agent/main.py +0 -0
  9. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_host_agent/models.py +0 -0
  10. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_host_agent/routers/__init__.py +0 -0
  11. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_host_agent/routers/update.py +0 -0
  12. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager.egg-info/SOURCES.txt +0 -0
  13. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager.egg-info/dependency_links.txt +0 -0
  14. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager.egg-info/entry_points.txt +0 -0
  15. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager.egg-info/requires.txt +0 -0
  16. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager.egg-info/top_level.txt +0 -0
  17. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager_cli/__init__.py +0 -0
  18. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager_cli/config/config.yml +0 -0
  19. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/cyclo_manager_cli/docker/docker-compose.yml +0 -0
  20. {cyclo_manager-0.2.0.dev3 → cyclo_manager-0.2.0.dev5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 0.2.0.dev3
3
+ Version: 0.2.0.dev5
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
@@ -19,10 +19,8 @@
19
19
  """Repository management endpoints."""
20
20
 
21
21
  import asyncio
22
- import json
23
22
  import os
24
23
  import re
25
- import urllib.request
26
24
  from pathlib import Path
27
25
  from typing import Optional
28
26
 
@@ -46,9 +44,9 @@ GIT_TIMEOUT = 120.0
46
44
 
47
45
 
48
46
  def _resolve_home() -> Path:
49
- sudo_user = os.environ.get('SUDO_USER')
50
- if sudo_user:
51
- return Path(f'/home/{sudo_user}')
47
+ user_home = os.environ.get('CYCLO_MANAGER_USER_HOME')
48
+ if user_home:
49
+ return Path(user_home)
52
50
  return Path.home()
53
51
 
54
52
 
@@ -87,8 +85,10 @@ def _fmt_cmd(cmd: str, output: str) -> str:
87
85
  # ── git helpers ────────────────────────────────────────────────────────────────
88
86
 
89
87
  async def _git(args: list[str], cwd: Optional[Path] = None) -> tuple[int, str, str]:
88
+ user = os.environ.get('CYCLO_MANAGER_USER')
89
+ cmd = ['runuser', '-u', user, '--', 'git', *args] if user else ['git', *args]
90
90
  proc = await asyncio.create_subprocess_exec(
91
- 'git', *args,
91
+ *cmd,
92
92
  cwd=str(cwd) if cwd else None,
93
93
  stdout=asyncio.subprocess.PIPE,
94
94
  stderr=asyncio.subprocess.PIPE,
@@ -133,21 +133,14 @@ def _is_newer(latest: str, current: str) -> bool:
133
133
  return _parse_version(latest) > _parse_version(current)
134
134
 
135
135
 
136
- def _parse_github_slug(remote_url: str) -> Optional[str]:
137
- https_match = re.match(r'https?://github\.com/([^/]+/[^/]+?)(?:\.git)?/?$', remote_url)
136
+ def _is_managed_remote(remote_url: str) -> bool:
137
+ https_match = re.match(r'https?://github\.com/([^/]+)/', remote_url)
138
138
  if https_match:
139
- return https_match.group(1)
140
- ssh_match = re.match(r'git@github\.com:([^/]+/[^/]+?)(?:\.git)?$', remote_url)
139
+ return https_match.group(1).lower() == MANAGED_GITHUB_ORG.lower()
140
+ ssh_match = re.match(r'git@github\.com:([^/]+)/', remote_url)
141
141
  if ssh_match:
142
- return ssh_match.group(1)
143
- return None
144
-
145
-
146
- def _is_managed_remote(remote_url: str) -> bool:
147
- slug = _parse_github_slug(remote_url)
148
- if not slug:
149
- return False
150
- return slug.split('/')[0].lower() == MANAGED_GITHUB_ORG.lower()
142
+ return ssh_match.group(1).lower() == MANAGED_GITHUB_ORG.lower()
143
+ return False
151
144
 
152
145
 
153
146
  def _read_package_xml_version(repo_path: Path) -> Optional[str]:
@@ -164,15 +157,18 @@ def _read_package_xml_version(repo_path: Path) -> Optional[str]:
164
157
  return None
165
158
 
166
159
 
167
- def _fetch_github_latest(slug: str) -> Optional[str]:
168
- url = f'https://api.github.com/repos/{slug}/releases/latest'
169
- req = urllib.request.Request(url, headers={'User-Agent': 'cyclo-manager/1.0'})
170
- try:
171
- with urllib.request.urlopen(req, timeout=10) as resp:
172
- data = json.loads(resp.read().decode())
173
- return (data.get('tag_name') or '').strip() or None
174
- except Exception:
160
+ async def _fetch_latest_tag(remote_url: str) -> Optional[str]:
161
+ rc, out, _ = await _git(['ls-remote', '--tags', remote_url])
162
+ if rc != 0:
163
+ return None
164
+ tags = []
165
+ for line in out.splitlines():
166
+ if 'refs/tags/' not in line or '^{}' in line:
167
+ continue
168
+ tags.append(line.split('refs/tags/')[-1].strip())
169
+ if not tags:
175
170
  return None
171
+ return max(tags, key=_parse_version)
176
172
 
177
173
 
178
174
  async def _repo_update_status(repo_path: Path) -> Optional[RepoUpdateStatus]:
@@ -182,12 +178,11 @@ async def _repo_update_status(repo_path: Path) -> Optional[RepoUpdateStatus]:
182
178
  if not remote_url or not _is_managed_remote(remote_url):
183
179
  return None
184
180
 
185
- slug = _parse_github_slug(remote_url)
186
181
  current = _read_package_xml_version(repo_path)
187
182
  if not current:
188
183
  return RepoUpdateStatus(name=repo_path.name, has_update=False)
189
184
 
190
- latest = await asyncio.to_thread(_fetch_github_latest, slug)
185
+ latest = await _fetch_latest_tag(remote_url)
191
186
  if not latest:
192
187
  return RepoUpdateStatus(name=repo_path.name, current_version=current, has_update=False)
193
188
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cyclo-manager
3
- Version: 0.2.0.dev3
3
+ Version: 0.2.0.dev5
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
@@ -87,11 +87,6 @@ def _setup_socket_dir(user: str) -> bool:
87
87
  check=True,
88
88
  capture_output=True,
89
89
  )
90
- subprocess.run(
91
- ['sudo', 'chown', f'{user}:{user}', HOST_AGENT_SOCKET_DIR],
92
- check=True,
93
- capture_output=True,
94
- )
95
90
  subprocess.run(
96
91
  ['sudo', 'rm', '-f', HOST_AGENT_SOCKET],
97
92
  check=True,
@@ -134,10 +129,9 @@ After=network.target
134
129
 
135
130
  [Service]
136
131
  Type=simple
137
- User={user}
138
- Group={user}
139
132
  ExecStart={agent_exe}
140
- Environment=HOME={user_home}
133
+ Environment=CYCLO_MANAGER_USER={user}
134
+ Environment=CYCLO_MANAGER_USER_HOME={user_home}
141
135
  Restart=always
142
136
  RestartSec=3
143
137
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cyclo-manager"
7
- version = "0.2.0.dev3"
7
+ version = "0.2.0.dev5"
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"