blob: 5977c81e6e5cbdd5e8928a7697c4f7ee3697ce15 [file] [log] [blame]
# Lint as: python3
# Copyright 2021 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Helper module for retrieving git repository metadata."""
import datetime as dt
import functools
import pathlib
from typing import Optional, Union
from . import subprocess_utils
@functools.lru_cache(maxsize=1)
def get_chromium_src_path() -> pathlib.Path:
"""Returns the root 'src' absolute path of this Chromium Git checkout.
Example Path: /home/username/git/chromium/src
Returns:
The absolute path to the 'src' root directory of the Chromium Git
checkout containing this file.
"""
_CHROMIUM_SRC_ROOT = pathlib.Path(__file__).parents[3].resolve(strict=True)
if _CHROMIUM_SRC_ROOT.name != 'src':
raise AssertionError(
f'_CHROMIUM_SRC_ROOT "{_CHROMIUM_SRC_ROOT}" should end in "src".')
try:
_assert_git_repository(_CHROMIUM_SRC_ROOT)
except (ValueError, RuntimeError):
raise AssertionError
return _CHROMIUM_SRC_ROOT
def get_head_commit_hash(git_repo: Optional[Union[str, pathlib.Path]] = None
) -> str:
"""Gets the hash of the commit at HEAD for a Git repository.
This returns the full, non-abbreviated, SHA1 hash of the commit as a string
containing 40 hexadecimal characters. For example,
'632918ad686949a9bc5f17ee1b48fa48e81be645'.
Args:
git_repo:
The path to a Git repository's root directory; if not specified,
defaults to the Chromium Git repository.
Returns:
The SHA1 hash of the Git repository's commit at HEAD.
Raises
ValueError:
The path specified in the git_repo parameter is not a root
directory for a Git repository.
RuntimeError:
The path specified in the git_repo parameter contains an infinite
loop.
"""
if not git_repo:
git_repo = get_chromium_src_path()
if not isinstance(git_repo, pathlib.Path):
git_repo = pathlib.Path(git_repo)
_assert_git_repository(git_repo)
return subprocess_utils.run_command(
['git', 'show', '--no-patch', f'--pretty=format:%H'], cwd=git_repo)
def get_head_commit_datetime(
git_repo: Optional[Union[str, pathlib.Path]] = None) -> dt.datetime:
"""Gets the datetime of the commit at HEAD for a Git repository in UTC.
The datetime returned contains timezone information (in timezone.utc) so
that it can be easily be formatted or converted (e.g., to local time) based
on the caller's needs.
Args:
git_repo:
The path to a Git repository's root directory; if not specified,
defaults to the Chromium Git repository.
Returns:
The datetime of the Git repository's commit at HEAD.
Raises
ValueError:
The path specified in the git_repo parameter is not a root
directory for a Git repository.
RuntimeError:
The path specified in the git_repo parameter contains an infinite
loop.
"""
if not git_repo:
git_repo = get_chromium_src_path()
if not isinstance(git_repo, pathlib.Path):
git_repo = pathlib.Path(git_repo)
_assert_git_repository(git_repo)
timestamp = subprocess_utils.run_command(
['git', 'show', '--no-patch', '--format=%ct'], cwd=git_repo)
return dt.datetime.fromtimestamp(float(timestamp), tz=dt.timezone.utc)
def _assert_git_repository(git_repo_root: pathlib.Path) -> None:
try:
repo_path = git_repo_root.resolve(strict=True)
except FileNotFoundError as err:
raise ValueError(
f'The Git repository root "{git_repo_root}" is invalid;'
f' {err.strerror}: "{err.filename}".')
if not repo_path.is_dir():
raise ValueError(
f'The Git repository root "{git_repo_root}" is invalid;'
f' not a directory.')
try:
git_internals_path = repo_path.joinpath('.git').resolve(strict=True)
except FileNotFoundError as err:
raise ValueError(
f'The path "{git_repo_root}" is not a root directory for a Git'
f' repository; {err.strerror}: "{err.filename}".')
if not repo_path.is_dir():
raise ValueError(
f'The Git repository root "{git_repo_root}" is invalid;'
f' {git_internals_path} is not a directory.')