diff --git a/src/hive_cli/platform/base.py b/src/hive_cli/platform/base.py index e070581..bd97b72 100644 --- a/src/hive_cli/platform/base.py +++ b/src/hive_cli/platform/base.py @@ -9,6 +9,7 @@ from hive_cli.config import HiveConfig from hive_cli.runtime.runtime import Runtime from hive_cli.utils import git, image +from hive_cli.utils import time as utime from hive_cli.utils.logger import logger @@ -96,7 +97,7 @@ def prepare_images(self, config: HiveConfig, push: bool = False) -> str: ) dest = Path(temp_repo_dir) / "repo" - hash = git.get_codebase(config.repo.source, str(dest), config.repo.branch) + git.get_codebase(config.repo.source, str(dest), config.repo.branch) logger.debug( f"Cloning repository {config.repo.source} to {dest}, the tree structure of the directory: {os.listdir('.')}, the tree structure of the {dest} directory: {os.listdir(dest)}" ) @@ -144,8 +145,8 @@ def prepare_images(self, config: HiveConfig, push: bool = False) -> str: "Unsupported cloud provider configuration. Please enable GCP or AWS." ) - # Use the git commit hash as the image tag to ensure uniqueness. - image_name = f"{image_registry}:{hash[:7]}" + tag = utime.now_us() + image_name = f"{image_registry}:{tag}" logger.debug( f"Building sandbox image {image_name} in {temp_sandbox_dir} with push={push}" diff --git a/src/hive_cli/utils/git.py b/src/hive_cli/utils/git.py index 0c8f3fb..46fef20 100644 --- a/src/hive_cli/utils/git.py +++ b/src/hive_cli/utils/git.py @@ -4,19 +4,16 @@ import git -from hive_cli.utils import time as utime from hive_cli.utils.logger import logger -def get_codebase(source: str, dest: str, branch: str = "main") -> str: +def get_codebase(source: str, dest: str, branch: str = "main") -> None: """ Copy/clone repository from the given source to the destination directory. Args: source (str): The URL or path of the repository to clone. dest (str): The directory where the repository will be cloned. branch (str): The branch to checkout after cloning. Default is "main". - Returns: - str: The commit hash of the cloned repository. """ # Case `source` is a URL, we clone it. if source.startswith("https://"): @@ -39,18 +36,3 @@ def get_codebase(source: str, dest: str, branch: str = "main") -> str: logger.debug(f"Copying repository from {source} to {dest}") shutil.copytree(source_path, dest, dirs_exist_ok=True) - # Get the current commit hash if it's a git repository. - if os.path.exists(os.path.join(source_path, ".git")): - repo = git.Repo(source_path) - else: - # If not a repo, return a timestamp-based identifier. - logger.warning( - f"Source path {source} is not a git repository. Using timestamp as hash." - ) - return utime.now_2_hash() - try: - code_version_id = repo.head.commit.hexsha - except Exception as e: - raise ValueError(f"Repository at {dest} has no commits yet: {e}") from e - logger.debug(f"Repository copied successfully with commit ID {code_version_id}") - return code_version_id diff --git a/src/hive_cli/utils/time.py b/src/hive_cli/utils/time.py index 1c9a66d..493f7a3 100644 --- a/src/hive_cli/utils/time.py +++ b/src/hive_cli/utils/time.py @@ -18,6 +18,10 @@ def humanize_time(timestamp: str) -> str: return age +def now_us() -> str: + return str(int(datetime.now(timezone.utc).timestamp() * 1_000_000)) + + def now_2_hash() -> str: timestamp = str(int(datetime.now(timezone.utc).timestamp())) unique_hash = hashlib.sha1(timestamp.encode()).hexdigest()[:7] diff --git a/tests/utils/test_git.py b/tests/utils/test_git.py index 5a88f1b..a746cfa 100644 --- a/tests/utils/test_git.py +++ b/tests/utils/test_git.py @@ -16,20 +16,17 @@ def __init__(self, hexsha): class _MockHead: - def __init__(self, hexsha=HEXSHA, raise_on_access=False): + def __init__(self, hexsha=HEXSHA): self._hexsha = hexsha - self._raise = raise_on_access @property def commit(self): - if self._raise: - raise RuntimeError("no commit") return _MockCommit(self._hexsha) class _MockRepo: - def __init__(self, hexsha=HEXSHA, raise_on_access=False): - self.head = _MockHead(hexsha=hexsha, raise_on_access=raise_on_access) + def __init__(self, hexsha=HEXSHA): + self.head = _MockHead(hexsha=hexsha) self.checked_out = None self.git = types.SimpleNamespace( checkout=lambda branch: setattr(self, "checked_out", branch) @@ -92,7 +89,7 @@ def test_clone_url_without_token(monkeypatch, tmp_path, mock_git): url = "https://github.com/org/repo.git" dest = tmp_path / "dest" - hexsha = get_codebase(url, str(dest), branch="develop") + get_codebase(url, str(dest), branch="develop") # clone_from called with original URL (no token injected) called_url, called_dest = mock_git["clone_args"] @@ -102,9 +99,6 @@ def test_clone_url_without_token(monkeypatch, tmp_path, mock_git): # checkout called with the given branch assert mock_git["repo"].checked_out == "develop" - # returned commit hash matches mock - assert hexsha == mock_git["repo"].head.commit.hexsha - def test_clone_url_with_token_injected(monkeypatch, tmp_path, mock_git): monkeypatch.setenv("GITHUB_TOKEN", "SECRET123") @@ -112,7 +106,7 @@ def test_clone_url_with_token_injected(monkeypatch, tmp_path, mock_git): url = "https://github.com/org/repo.git" dest = tmp_path / "dest" - _ = get_codebase(url, str(dest)) + get_codebase(url, str(dest)) called_url, _ = mock_git["clone_args"] assert called_url.startswith("https://x-access-token:SECRET123@") @@ -141,48 +135,19 @@ def test_local_git_repo_copy(monkeypatch, tmp_path, mock_git, mock_copytree): dest = tmp_path / "dest" - hexsha = get_codebase(str(src), str(dest)) + get_codebase(str(src), str(dest)) # copytree was invoked with dirs_exist_ok=True assert mock_copytree["args"] == (src.resolve(), dest, True) - # git.Repo was constructed with the SOURCE path (not dest) - assert Path(mock_git["repo_arg"]).resolve() == src.resolve() - - # Returned hash is from the mock repo - assert hexsha == HEXSHA - -def test_local_non_git_returns_timestamp(tmp_path, mock_copytree, caplog): +def test_local_non_git_copy(tmp_path, mock_copytree): # No .git directory -> non-git path src = tmp_path / "src" src.mkdir() dest = tmp_path / "dest" - result = get_codebase(str(src), str(dest)) + get_codebase(str(src), str(dest)) # copytree still happens assert mock_copytree["args"] == (src.resolve(), dest, True) - - assert len(result) == 7 - - # warning logged (optional but nice to assert) - assert any("is not a git repository" in rec.getMessage() for rec in caplog.records) - - -def test_repo_without_commits_raises_valueerror(monkeypatch, tmp_path, mock_git): - import hive_cli.utils.git as target_module - - # Make clone_from return a repo whose head.commit access raises - def bad_clone(url, dest, *args, **kwargs): - return _MockRepo(raise_on_access=True) - - monkeypatch.delenv("GITHUB_TOKEN", raising=False) - monkeypatch.setattr(target_module.git.Repo, "clone_from", bad_clone, raising=True) - - url = "https://github.com/org/repo.git" - dest = tmp_path / "dest" - - with pytest.raises(ValueError) as exc: - target_module.get_codebase(url, str(dest)) - assert "has no commits yet" in str(exc.value) diff --git a/tests/utils/test_time.py b/tests/utils/test_time.py index fb20b4c..44bdc1d 100644 --- a/tests/utils/test_time.py +++ b/tests/utils/test_time.py @@ -1,23 +1,6 @@ -import hashlib from datetime import datetime, timezone from unittest.mock import patch -from src.hive_cli.utils.time import now_2_hash - - -def test_now_2_hash(): - fixed_timestamp = 1700000000 - fixed_datetime = datetime.fromtimestamp(fixed_timestamp, tz=timezone.utc) - - class FixedDateTime(datetime): - @classmethod - def now(cls, tz=None): - return fixed_datetime - - with patch("src.hive_cli.utils.time.datetime", FixedDateTime): - expected_hash = hashlib.sha1(str(fixed_timestamp).encode()).hexdigest()[:7] - assert now_2_hash() == expected_hash - def test_humanize_time(): from src.hive_cli.utils.time import humanize_time