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
3 changes: 2 additions & 1 deletion src/dvsim/flow/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
rm_path,
subst_wildcards,
)
from dvsim.utils.git import git_commit_hash
from dvsim.utils.git import git_commit_hash, git_is_dirty

if TYPE_CHECKING:
from dvsim.job.deploy import Deploy
Expand Down Expand Up @@ -173,6 +173,7 @@ def __init__(self, flow_cfg_file, hjson_data, args, mk_config) -> None:
proj_root = Path(self.proj_root)
self.commit = git_commit_hash(path=proj_root, short=False)
self.commit_short = git_commit_hash(path=proj_root, short=True)
self.dirty = git_is_dirty(path=proj_root)

# Construct the path variables after variable expansion.
reports_dir = Path(self.scratch_base_path) / "reports"
Expand Down
7 changes: 5 additions & 2 deletions src/dvsim/report/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ class IPMeta(BaseModel):
"""Git commit sha of the IP the tests are run against."""
commit_short: str
"""Shortened Git commit sha of the IP the tests are run against."""
dirty: bool = False
"""Whether the working tree had uncommitted changes at run time."""
branch: str
"""Git branch"""
url: str
"""URL to where the IP can be found in git (e.g. github)."""
url: str | None
"""URL to where the IP can be found in git (e.g. github). None if the local
checkout has no ``origin`` remote (typical for developer clones)."""
revision_info: str | None
"""Optional revision info string to use for custom info instead of the above fields."""

Expand Down
7 changes: 7 additions & 0 deletions src/dvsim/sim/flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,11 @@ def gen_results(self, results: Sequence[CompletedJobStatus]) -> None:
repo_root = Path(self.proj_root)
reports_dir = Path(self.scratch_base_path) / "reports"
url = git_https_url_with_commit(path=repo_root)
if url is None:
log.debug(
"no 'origin' remote in %s — report will not include an upstream source link",
repo_root,
)
build_seed = self.build_seed if not self.run_only else None

try:
Expand Down Expand Up @@ -657,6 +662,7 @@ def gen_results(self, results: Sequence[CompletedJobStatus]) -> None:
variant=self.variant,
commit=self.commit,
commit_short=self.commit_short,
dirty=self.dirty,
branch=self.branch,
url=url,
revision_info=self.revision,
Expand Down Expand Up @@ -705,6 +711,7 @@ def _gen_json_results(
variant=(self.variant or "").lower() or None,
commit=self.commit,
commit_short=self.commit_short,
dirty=self.dirty,
branch=self.branch or "",
url=url,
revision_info=self.revision,
Expand Down
7 changes: 6 additions & 1 deletion src/dvsim/sim/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,12 @@ def render_metadata(

revision = (scope.revision_info or "").strip()
if not revision:
revision = f"Github Revision: [`{scope.commit_short}`]({scope.url})"
if scope.url:
revision = f"Github Revision: [`{scope.commit_short}`]({scope.url})"
else:
revision = f"Revision: `{scope.commit_short}`"
if scope.dirty and "(dirty)" not in revision:
revision += " (dirty)"
report_md += f"\n### {revision}"
report_md += f"\n### Branch: {scope.branch}"

Expand Down
32 changes: 27 additions & 5 deletions src/dvsim/utils/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,19 @@ def git_commit_hash(path: Path | None = None, *, short: bool = False) -> str:
return r.head.commit.hexsha


def git_origin_url(path: Path | None = None) -> str:
"""Get the git remote origin url."""
def git_is_dirty(path: Path | None = None) -> bool:
"""Return True if the working tree has uncommitted changes to tracked files."""
root = repo_root(path=path or Path.cwd())

if root is None:
log.error("no git repo found at %s", path)
raise ValueError

return Repo(root).is_dirty()


def git_origin_url(path: Path | None = None) -> str | None:
"""Get the git remote origin url, or None if no ``origin`` remote is configured."""
root = repo_root(path=path or Path.cwd())

if root is None:
Expand All @@ -54,20 +65,31 @@ def git_origin_url(path: Path | None = None) -> str:

r = Repo(root)

return r.remote().url
if "origin" not in [remote.name for remote in r.remotes]:
return None

return r.remote("origin").url


def git_https_url_with_commit(path: Path | None = None) -> str:
def git_https_url_with_commit(path: Path | None = None) -> str | None:
"""Get an https url that references the current commit.

The link is derived from the ``origin`` remote, which in CI/regression
workflows points at the canonical upstream repository. In developer
checkouts where ``origin`` is absent or points elsewhere, return ``None``
rather than guessing — a wrong link is worse than none.

Args:
path: the path to the git repo

Returns:
str containing the https url
str containing the https url, or None if no ``origin`` remote exists.

"""
url = git_origin_url(path=path)
if url is None:
return None

commit = git_commit_hash(path=path)

url = url.removesuffix(".git")
Expand Down
41 changes: 37 additions & 4 deletions tests/utils/test_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
from git import Repo
from hamcrest import assert_that, calling, equal_to, raises

from dvsim.utils.git import git_commit_hash, git_https_url_with_commit, git_origin_url, repo_root
from dvsim.utils.git import (
git_commit_hash,
git_https_url_with_commit,
git_is_dirty,
git_origin_url,
repo_root,
)

__all__ = ()

Expand Down Expand Up @@ -73,11 +79,11 @@ def test_git_short_commit_hash(tmp_path: "Path") -> None:
)

@staticmethod
def test_git_origin_url(tmp_path: Path) -> None:
"""Test that the expected git remote origin url is returned."""
def test_git_is_dirty(tmp_path: Path) -> None:
"""Test that git_is_dirty reflects the working tree state."""
# Value error if called outside a git repo
assert_that(
calling(git_origin_url).with_args(tmp_path),
calling(git_is_dirty).with_args(tmp_path),
raises(ValueError),
)

Expand All @@ -88,12 +94,36 @@ def test_git_origin_url(tmp_path: Path) -> None:
r.index.add([file])
r.index.commit("initial commit")

# Clean tree
assert_that(git_is_dirty(tmp_path), equal_to(False))

# Modify a tracked file — now dirty
file.write_text("changed")
assert_that(git_is_dirty(tmp_path), equal_to(True))

@staticmethod
def test_git_origin_url(tmp_path: Path) -> None:
"""Test that the expected git remote origin url is returned."""
# Value error if called outside a git repo
assert_that(
calling(git_origin_url).with_args(tmp_path),
raises(ValueError),
)

r = Repo.init(path=tmp_path)

file = tmp_path / "a"
file.write_text("file to commit")
r.index.add([file])
r.index.commit("initial commit")

# None if the repo has no 'origin' remote configured
assert_that(git_origin_url(tmp_path), equal_to(None))

# Still None if there are remotes but none named 'origin'
r.create_remote("upstream", "git@github.com:lowRISC/other.git")
assert_that(git_origin_url(tmp_path), equal_to(None))

url = "git@github.com:lowRISC/test.git"
r.create_remote("origin", url)

Expand Down Expand Up @@ -122,6 +152,9 @@ def test_git_https_url_with_commit(tmp_path: Path, url: str, expected: str) -> N
r.index.add([file])
r.index.commit("initial commit")

# Returns None when no 'origin' remote is configured.
assert_that(git_https_url_with_commit(tmp_path), equal_to(None))

r.create_remote("origin", url)

commit = r.head.commit.hexsha
Expand Down
Loading