Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def read_version():
'pyyaml~=6.0',
'rich>=14',
'requests==2.31.0', # Docker SDK breaks on 2.32.0
'filelock==3.29.7',
'repo_library @ git+https://github.com/rdkcentral/sc-repo-library.git@master',
'git_flow_library @ git+https://github.com/rdkcentral/sc-git-flow-library.git@master',
'sc_manifest_parser @ git+https://github.com/rdkcentral/sc-manifest-parser.git@main'
Expand Down
57 changes: 55 additions & 2 deletions src/sc/clone/cloners/repo_cloner.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import shutil
import subprocess
import sys
import time

from filelock import SoftFileLock, Timeout
from pydantic import BaseModel

from .cloner import Cloner, RefType
Expand All @@ -29,6 +31,8 @@
logger = logging.getLogger(__name__)

REPO_CACHE_DIR = Path.home() / ".caches"
CACHE_LOCK_PATH = REPO_CACHE_DIR / ".sc_lock"
CACHE_MAX_WAIT = 300
Comment thread
BenjiMilan marked this conversation as resolved.

class RepoClonerConfig(BaseModel):
"""
Expand Down Expand Up @@ -74,8 +78,24 @@ def clone(self, directory: Path):
- Parses the manifest to retrieve projects.
- Initializes GitFlow for all unlocked projects.
"""
reference = self._cache() if self.config.cache else None
if self.config.cache:
self._clone_with_cache(directory)
else:
self._clone(directory)

def _clone_with_cache(self, directory: Path):
REPO_CACHE_DIR.mkdir(exist_ok=True)

lock = SoftFileLock(CACHE_LOCK_PATH)
self._acquire_cache_lock(lock)

try:
reference = self._cache()
self._clone(directory, reference)
finally:
Comment thread
BenjiMilan marked this conversation as resolved.
lock.release()

def _clone(self, directory: Path, reference: Path | None = None):
self._init_repo(directory=directory, reference=reference)
RepoLibrary.sync(
directory,
Expand All @@ -95,7 +115,6 @@ def _cache(self) -> Path:
Returns:
Path: The directory of the mirrored cache.
"""
REPO_CACHE_DIR.mkdir(exist_ok=True)
manifest_hostname = self._get_manifest_hostname(self.config.uri)
host_cache_dir = Path(REPO_CACHE_DIR / manifest_hostname)
host_cache_dir.mkdir(exist_ok=True)
Expand Down Expand Up @@ -139,6 +158,40 @@ def _init_repo(self, directory: Path, mirror: bool = False, reference: Path | No
logger.error(f"repo init error: {e}")
sys.exit(1)

def _acquire_cache_lock(self, lock: SoftFileLock):
"""Try to acquire cache lock. After a certain time bypass it."""
started = time.monotonic()
first_warning = False

logger.info("Acquiring cache lock.")
while True:
try:
lock.acquire(timeout=20)
logger.info("Cache lock acquired.")
return

except Timeout:
waited = int(time.monotonic() - started)
if waited < CACHE_MAX_WAIT:
logger.info(
f"Cache is in use by another process, waited {waited} seconds..."
)

if first_warning == False:
logger.info(f"SC will wait for {CACHE_MAX_WAIT} seconds before bypassing.")
logger.info(
"The cache is user specific, if you believe there is no way your cache "
f"should be in use you could try deleting the lock: {CACHE_LOCK_PATH}")
first_warning = True
else:
logger.warning(
f"Cache remained locked past the set wait time of {CACHE_MAX_WAIT} seconds. "
"Force proceeding without acquiring cache lock..."
)
lock.break_lock()
lock.acquire()
return

def _get_manifest_hostname(self, url: str) -> str:
"""Extracts the hostname from a given URL.

Expand Down