|
5 | 5 | import json |
6 | 6 | import os |
7 | 7 | import re |
8 | | -import shutil |
9 | | -import subprocess |
10 | 8 | from pathlib import Path |
11 | 9 | from typing import TYPE_CHECKING |
12 | 10 |
|
13 | 11 | from fastmcp.mcp_config import MCPConfig |
14 | 12 |
|
15 | 13 | from openhands.sdk.context.skills.exceptions import SkillValidationError |
| 14 | +from openhands.sdk.git.cached_repo import try_cached_clone_or_update |
16 | 15 | from openhands.sdk.logger import get_logger |
17 | 16 |
|
18 | 17 |
|
@@ -316,77 +315,19 @@ def update_skills_repository( |
316 | 315 | ) -> Path | None: |
317 | 316 | """Clone or update the local skills repository. |
318 | 317 |
|
| 318 | + Uses the shared git caching infrastructure from openhands.sdk.git.cached_repo. |
| 319 | + When updating, performs: fetch -> checkout ref -> reset --hard to origin/ref. |
| 320 | +
|
319 | 321 | Args: |
320 | 322 | repo_url: URL of the skills repository. |
321 | | - branch: Branch name to use. |
| 323 | + branch: Branch name to checkout and track. |
322 | 324 | cache_dir: Directory where the repository should be cached. |
323 | 325 |
|
324 | 326 | Returns: |
325 | 327 | Path to the local repository if successful, None otherwise. |
326 | 328 | """ |
327 | 329 | repo_path = cache_dir / "public-skills" |
328 | | - |
329 | | - try: |
330 | | - if repo_path.exists() and (repo_path / ".git").exists(): |
331 | | - logger.debug(f"Updating skills repository at {repo_path}") |
332 | | - try: |
333 | | - subprocess.run( |
334 | | - ["git", "fetch", "origin"], |
335 | | - cwd=repo_path, |
336 | | - check=True, |
337 | | - capture_output=True, |
338 | | - timeout=30, |
339 | | - ) |
340 | | - subprocess.run( |
341 | | - ["git", "reset", "--hard", f"origin/{branch}"], |
342 | | - cwd=repo_path, |
343 | | - check=True, |
344 | | - capture_output=True, |
345 | | - timeout=10, |
346 | | - ) |
347 | | - logger.debug("Skills repository updated successfully") |
348 | | - except subprocess.TimeoutExpired: |
349 | | - logger.warning("Git pull timed out, using existing cached repository") |
350 | | - except subprocess.CalledProcessError as e: |
351 | | - logger.warning( |
352 | | - f"Failed to update repository: {e.stderr.decode()}, " |
353 | | - f"using existing cached version" |
354 | | - ) |
355 | | - else: |
356 | | - logger.info(f"Cloning public skills repository from {repo_url}") |
357 | | - if repo_path.exists(): |
358 | | - shutil.rmtree(repo_path) |
359 | | - |
360 | | - subprocess.run( |
361 | | - [ |
362 | | - "git", |
363 | | - "clone", |
364 | | - "--depth", |
365 | | - "1", |
366 | | - "--branch", |
367 | | - branch, |
368 | | - repo_url, |
369 | | - str(repo_path), |
370 | | - ], |
371 | | - check=True, |
372 | | - capture_output=True, |
373 | | - timeout=60, |
374 | | - ) |
375 | | - logger.debug(f"Skills repository cloned to {repo_path}") |
376 | | - |
377 | | - return repo_path |
378 | | - |
379 | | - except subprocess.TimeoutExpired: |
380 | | - logger.warning(f"Git operation timed out for {repo_url}") |
381 | | - return None |
382 | | - except subprocess.CalledProcessError as e: |
383 | | - logger.warning( |
384 | | - f"Failed to clone/update repository {repo_url}: {e.stderr.decode()}" |
385 | | - ) |
386 | | - return None |
387 | | - except Exception as e: |
388 | | - logger.warning(f"Error managing skills repository: {str(e)}") |
389 | | - return None |
| 330 | + return try_cached_clone_or_update(repo_url, repo_path, ref=branch, update=True) |
390 | 331 |
|
391 | 332 |
|
392 | 333 | def discover_skill_resources(skill_dir: Path) -> SkillResources: |
|
0 commit comments