Source code for foundry_dev_tools.utils.repo
"""This file provides helper function for git repos."""
from __future__ import annotations
import logging
import subprocess
import warnings
from pathlib import Path
from foundry_dev_tools.errors.meta import FoundryDevToolsError
LOGGER = logging.getLogger(__name__)
[docs]
def get_repo(repo_dir: Path | None = None) -> tuple[str, str, str, Path]:
"""Get the repository RID of the current working directory.
Args:
repo_dir: the path to a (sub)directory of a git repo,
otherwise current working directory
Returns:
tuple[str,str,str]: repo_rid,git_ref,git_revision_hash
"""
if git_dir := git_toplevel_dir(repo_dir, use_git=True):
gradle_props = git_dir.joinpath("gradle.properties")
if gradle_props.is_file():
try:
with gradle_props.open("r") as gpf:
for line in gpf.readlines():
if line.startswith("transformsRepoRid"):
return (
line.split("=")[1].strip(),
get_git_ref(git_dir),
get_git_revision_hash(git_dir),
git_dir,
)
except Exception as e:
msg = "Can't get repository RID from the gradle.properties file. Malformed file?"
raise FoundryDevToolsError(msg) from e
msg = "Can't get repository RID from the gradle.properties file. Is this really a foundry repository?"
raise FoundryDevToolsError(
msg,
)
msg = "There is no gradle.properties file at the top of the git repository, can't get repository RID."
raise FoundryDevToolsError(
msg,
)
msg = (
"If you don't provide a repository RID you need to be in a repository directory to detect what you want to"
" build."
)
raise FoundryDevToolsError(
msg,
)
[docs]
def get_git_ref(git_dir: Path | None = None) -> str:
"""Get the branch ref in the supplied git directory.
Args:
git_dir: the path to a (sub)directory of a git repo,
otherwise current working directory
"""
return (
subprocess.check_output(
[
"git",
"symbolic-ref",
"HEAD",
],
cwd=git_dir,
)
.decode("ascii")
.strip()
)
[docs]
def get_git_revision_hash(git_dir: Path | None = None) -> str:
"""Get the git revision hash.
Args:
git_dir: the path to a (sub)directory of a git repo,
otherwise current working directory
"""
return (
subprocess.check_output(
[
"git",
"rev-parse",
"HEAD",
],
cwd=git_dir,
)
.decode("ascii")
.strip()
)
[docs]
def git_toplevel_dir(git_dir: Path | None = None, use_git: bool = False) -> Path | None:
"""Get git top level directory.
Args:
git_dir: the path to a (sub)directory of a git repo,
otherwise current working directory
use_git: if true call git executable with subprocess,
otherwise use minimal python only implementation
Returns:
Path | None: the path to the toplevel git directory or
None if nothing was found.
"""
if use_git:
try:
return Path(
subprocess.check_output(["git", "rev-parse", "--show-toplevel"], cwd=git_dir).decode("utf-8").strip(),
)
except subprocess.CalledProcessError:
pass
if git_dir is None:
git_dir = Path.cwd()
if git_dir.joinpath(".git").exists():
return git_dir
for p in git_dir.resolve().parents:
if p.joinpath(".git").exists():
return p
return None
[docs]
def get_branch(caller_filename: Path) -> str:
"""Get name of current checked out git branch as defined in the git HEAD file.
Args:
caller_filename (Path): Path of file that calls the function
Returns:
str: Name of checked out branch
"""
git_dir = git_toplevel_dir(caller_filename)
if not git_dir:
# fallback for VS Interactive Console
# or Jupyter lab on Windows
git_dir = Path.cwd()
if git_dir.joinpath(".git").is_file():
# Infer branch from git submodule
with git_dir.joinpath(".git").open() as gf:
rel_submodule_git_dir = gf.read().strip().replace("gitdir: ", "")
head_file = git_dir.joinpath(rel_submodule_git_dir, "HEAD").resolve()
else:
head_file = git_dir.joinpath(".git", "HEAD")
if head_file.is_file():
with head_file.open() as hf:
ref = hf.read().strip()
if ref.startswith("ref: refs/heads/"):
return ref[16:]
return "HEAD" # immitate behaviour of `git rev-parse --abbrev-ref HEAD`
warnings.warn("Could not detect git branch of project, falling back to 'master'.")
return "master"