Add script for doing git bisect using nightly snapshots#1595
Add script for doing git bisect using nightly snapshots#1595tstellar wants to merge 18 commits intofedora-llvm-team:mainfrom
Conversation
This script will perform a bisect between two git commits first using the rpm builds from the nightly snapshots and then builds from source.
tuliom
left a comment
There was a problem hiding this comment.
I have 3 suggestions.
The code LGTM overall.
| # Enable the copr repo that we want to test. | ||
| # FIXME: There is probably some way to do this via the python API, but I | ||
| # can't figure it out. | ||
| subprocess.run(["dnf", "copr", "enable", "-y", copr_fullname]) |
There was a problem hiding this comment.
Try this:
base = dnf.Base()
base.read_all_repos()
base.fill_sack()
repos = base.repos.get_matching('copr:' + copr_project)
repos.enable()
There was a problem hiding this comment.
This works, but you have to first download the .repo file from COPR.
There was a problem hiding this comment.
I see the problem now. Calling dnf copr is a simpler API.
| # Disable project so future installs don't use it. | ||
| # FIXME: There is probably some way to do this via the python API, but I | ||
| # can't figure it out. | ||
| subprocess.run(["dnf", "copr", "disable", "-y", copr_fullname]) |
There was a problem hiding this comment.
Likewise, but use repos.disable() instead.
| class CoprProject: | ||
| UNTESTED = 0 | ||
| GOOD = 1 | ||
| BAD = 2 | ||
|
|
||
| def __init__(self, name: str): | ||
| self.name = name | ||
| self.index = -1 | ||
| self._status = CoprProject.UNTESTED | ||
|
|
||
| def __lt__(self, other: Self) -> bool: | ||
| return self.name < other.name | ||
|
|
||
| @property | ||
| def commit(self) -> str: | ||
| return self._commit | ||
|
|
||
| @commit.setter | ||
| def commit(self, commit: str) -> None: | ||
| self._commit = commit | ||
|
|
||
| @property | ||
| def status(self) -> int: | ||
| return self._status | ||
|
|
||
| @status.setter | ||
| def status(self, status: int) -> None: | ||
| self._status = status |
There was a problem hiding this comment.
I wonder if this usage of an IntEnum that is a subclass of int can help here to make the code more "readable". Also, if you make your CoprProject a dataclass you can define defaults and access the members. Usually a name: str = "default" is enough but since you explicitly want to only order by name we have to say which field participates in the comparison operators like le. I hope this makes sense.
| class CoprProject: | |
| UNTESTED = 0 | |
| GOOD = 1 | |
| BAD = 2 | |
| def __init__(self, name: str): | |
| self.name = name | |
| self.index = -1 | |
| self._status = CoprProject.UNTESTED | |
| def __lt__(self, other: Self) -> bool: | |
| return self.name < other.name | |
| @property | |
| def commit(self) -> str: | |
| return self._commit | |
| @commit.setter | |
| def commit(self, commit: str) -> None: | |
| self._commit = commit | |
| @property | |
| def status(self) -> int: | |
| return self._status | |
| @status.setter | |
| def status(self, status: int) -> None: | |
| self._status = status | |
| import dataclasses | |
| import enum | |
| @enum.unique | |
| class CoprProjectStatus(enum.IntEnum): | |
| UNTESTED = 0 | |
| GOOD = 1 | |
| BAD = 2 | |
| @dataclasses.dataclass(kw_only=True, order=True) | |
| class CoprProject: | |
| name: str = field(compare=True) | |
| index: int = field(compare=False, default=-1) | |
| status: CoprProjectStatus = field(compare=False, default=CoprProjectStatus.UNTESTED) | |
| commit: str | None = field(compare=False, default=None) |
|
|
||
|
|
||
| def get_snapshot_projects(chroot: str | None = None) -> list[CoprProject]: | ||
| copr_client = copr.v3.Client.create_from_config_file() |
There was a problem hiding this comment.
Please, know that we have a function for this here as well.
| self._status = status | ||
|
|
||
|
|
||
| def get_snapshot_projects(chroot: str | None = None) -> list[CoprProject]: |
There was a problem hiding this comment.
Can you please put a comment here? Does the function get all snapshot projects for a given chroot ordered by name.
| continue | ||
| if chroot and chroot not in list(p.chroot_repos.keys()): | ||
| continue | ||
| projects.append(CoprProject(p.name)) |
There was a problem hiding this comment.
If you accept my suggestion on the dataclass above you're gonna have to make use of kw arguments here:
| projects.append(CoprProject(p.name)) | |
| projects.append(CoprProject(name=p.name)) |
| for idx, p in enumerate(projects): | ||
| p.index = idx | ||
| return projects |
There was a problem hiding this comment.
Why do you need an explicit index here?
There was a problem hiding this comment.
| m = regex.search(b["source_package"]["url"]) | ||
| if m: | ||
| return m.group(1) | ||
| raise Exception(f"Could not find commit for {project_name}, {chroot}") |
There was a problem hiding this comment.
| raise Exception(f"Could not find commit for {project_name}, {chroot}") | |
| raise Exception(f"Could not find commit for {project.name}, {chroot}") |
| projects = get_snapshot_projects() | ||
| good_project = None | ||
| bad_project = None | ||
|
|
There was a problem hiding this comment.
I suggest you create this helper function to eliminate the need to repeat all non-changing arguments. You only change the good/bad commit parts, right?
| def git_bisect_helper(good_commit:str, bad_commit=str): | |
| return git_bisect( | |
| repo, | |
| good_commit, | |
| bad_commit, | |
| args.configure_command, | |
| args.build_command, | |
| args.test_command, | |
| ) |
| return git_bisect( | ||
| repo, | ||
| args.good_commit, | ||
| p.commit, | ||
| args.configure_command, | ||
| args.build_command, | ||
| args.test_command, | ||
| ) |
There was a problem hiding this comment.
| return git_bisect( | |
| repo, | |
| args.good_commit, | |
| p.commit, | |
| args.configure_command, | |
| args.build_command, | |
| args.test_command, | |
| ) | |
| return git_bisect_helper(good_commit=args.good_commit, bad_commit=p.commit) |
| return git_bisect( | ||
| repo, | ||
| p.commit, | ||
| args.bad_commit, | ||
| args.configure_command, | ||
| args.build_command, | ||
| args.test_command, | ||
| ) |
There was a problem hiding this comment.
| return git_bisect( | |
| repo, | |
| p.commit, | |
| args.bad_commit, | |
| args.configure_command, | |
| args.build_command, | |
| args.test_command, | |
| ) | |
| return git_bisect_helper(good_commit=p.commit, bad_commit=args.bad_commit) |
| return git_bisect( | ||
| repo, | ||
| args.good_commit, | ||
| args.bad_commit, | ||
| args.configure_command, | ||
| args.build_command, | ||
| args.test_command, | ||
| ) |
There was a problem hiding this comment.
| return git_bisect( | |
| repo, | |
| args.good_commit, | |
| args.bad_commit, | |
| args.configure_command, | |
| args.build_command, | |
| args.test_command, | |
| ) | |
| return git_bisect_helper(good_commit=args.good_commit, bad_commit=args.bad_commit) |
This script will perform a bisect between two git commits first using the rpm builds from the nightly snapshots and then builds from source.