75 lines
2.7 KiB
Python
75 lines
2.7 KiB
Python
"""projectutility.core.submodule_manager
|
|
|
|
Utilities to ensure Git submodules are initialized/updated for a tool repo.
|
|
|
|
This module uses the system `git` command via subprocess to avoid
|
|
adding a hard dependency on GitPython for submodule operations.
|
|
"""
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
from typing import List, Optional, Dict
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def ensure_submodules(
|
|
repo_root: str, submodules: Optional[List[Dict[str, str]]] = None, timeout: int = 300
|
|
) -> bool:
|
|
"""Ensure submodules are initialized and updated for a repository.
|
|
|
|
Args:
|
|
repo_root: absolute path to the repository root (where .git lives).
|
|
submodules: optional list of dicts describing submodules. Each dict may
|
|
contain a `path` key (relative path inside repo). If None,
|
|
this will attempt to update the whole `external` directory
|
|
if it exists, otherwise run a global submodule update.
|
|
timeout: max seconds to wait for the git command.
|
|
|
|
Returns:
|
|
True if git submodule update completed successfully, False otherwise.
|
|
"""
|
|
if not os.path.isdir(repo_root):
|
|
logger.error("Submodule update: repo_root not found: %s", repo_root)
|
|
return False
|
|
|
|
cmd = ["git", "-C", repo_root, "submodule", "update", "--init", "--recursive"]
|
|
|
|
# Build path list for git command
|
|
if submodules:
|
|
paths = []
|
|
for entry in submodules:
|
|
p = entry.get("path") if isinstance(entry, dict) else None
|
|
if p:
|
|
paths.append(p)
|
|
if paths:
|
|
cmd += paths
|
|
else:
|
|
# Fallback: if repo has 'external' directory, limit to it
|
|
external = os.path.join(repo_root, "external")
|
|
if os.path.isdir(external):
|
|
cmd += ["external"]
|
|
|
|
logger.info("Running submodule update: %s", " ".join(cmd))
|
|
try:
|
|
proc = subprocess.run(
|
|
cmd, check=True, capture_output=True, text=True, timeout=timeout
|
|
)
|
|
if proc.stdout:
|
|
logger.debug("git submodule stdout: %s", proc.stdout)
|
|
if proc.stderr:
|
|
logger.debug("git submodule stderr: %s", proc.stderr)
|
|
logger.info("Submodule update completed for %s", repo_root)
|
|
return True
|
|
except subprocess.CalledProcessError as e:
|
|
logger.error(
|
|
"git submodule update failed for %s: %s", repo_root, e.stderr or str(e)
|
|
)
|
|
return False
|
|
except subprocess.TimeoutExpired:
|
|
logger.error("git submodule update timeout for %s", repo_root)
|
|
return False
|
|
except Exception as e:
|
|
logger.exception("Unexpected error while updating submodules for %s: %s", repo_root, e)
|
|
return False
|