123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- #
- # Copyright (c) Contributors to the Open 3D Engine Project.
- # For complete copyright and license terms please see the LICENSE at the root of this distribution.
- #
- # SPDX-License-Identifier: Apache-2.0 OR MIT
- #
- #
- import argparse
- import os
- import sys
- from typing import Dict, List
- import difflib
- from commit_validation.commit_validation import Commit, validate_commit
- import git
- class GitChange(Commit):
- """An implementation of the :class:`Commit` interface for accessing details about a git change"""
- def __init__(self, source: str = None, target: str = 'origin/main') -> None:
- """Creates a new instance of :class:`GitChange`
- :param source: source of the change, e.g. commit hash or branch
- :param target: target of the change, e.g. main branch
- """
- root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
- self.repo = git.Repo()
- self.git_root_path = self.repo.git.rev_parse("--show-toplevel")
- if source:
- git.cmd.Git().fetch('origin', source) # So we can compare it
- self.source_commit = self.repo.commit(target)
- else:
- self.source_commit = self.repo.commit()
- if 'origin' in target:
- origin_target = target.replace('origin/','')
- git.cmd.Git().fetch('origin', origin_target) # So we can compare it
- self.target_commit = self.repo.commit(target)
- # We only want to run the verification on the changes introduced by the
- # branch being merged in. Find the merge base (the common ancestor) of
- # the two commits, and then get the diff from merge_base..target_commit
- self.merge_base = self.repo.merge_base(self.source_commit, self.target_commit)
- if not len(self.merge_base) == 1:
- raise RuntimeError(f"Cannot find the merge base of {self.source_commit} and {self.target_commit}")
- self.merge_base = self.merge_base[0]
- self.diff_index = self.merge_base.diff(self.source_commit)
- print(f"Running validation from '{source}' ({self.source_commit}) to '{target}' ({self.target_commit}) using baseline {self.merge_base}")
- # Cache the file lists since they are requested by each validator
- self.files_list: List[str] = []
- self.removed_files_list: List[str] = []
- for diff_item in self.diff_index:
- # 'A' for added paths
- # 'C' for changed paths
- # 'R' for renamed paths
- # 'M' for paths with modified data
- # 'T' for changed in the type paths
- # 'D' for deleted paths
- # 'R' for renamed paths
- if diff_item.change_type in ('A', 'C', 'R', 'M', 'T'):
- self.files_list.append(os.path.abspath(os.path.join(self.git_root_path, diff_item.b_path)))
- if diff_item.change_type in ('D', 'R'):
- self.removed_files_list.append(os.path.abspath(os.path.join(self.git_root_path, diff_item.a_path)))
- def get_files(self) -> List[str]:
- """Returns a list of local files added/modified by the commit"""
- return self.files_list
- def get_removed_files(self) -> List[str]:
- """Returns a list of local files removed by the commit"""
- return self.removed_files_list
- def get_file_diff(self, str) -> str:
- """
- Given a file name, returns a string in unified diff format
- that represents the changes made to that file for this commit.
- Most validators will only pay attention to added lines (with + in front)
- """
- diff = self.repo.git.diff(self.merge_base, self.source_commit, str)
- return diff
- def get_description(self) -> str:
- """Returns the description of the commit"""
- return self.target_commit.message
- def get_author(self) -> str:
- """Returns the author of the commit"""
- return self.target_commit.author
- def init_parser():
- """Prepares the command line parser"""
- parser = argparse.ArgumentParser()
- parser.add_argument('--source', default=None, help='Change source (e.g. commit hash or branch), defaults to active branch')
- parser.add_argument('--target', default='origin/main', help='Change target, defaults to "origin/main"')
- return parser
- def main():
- parser = init_parser()
- args = parser.parse_args()
- change = GitChange(source=args.source, target=args.target)
- if not validate_commit(commit=change, ignore_validators=["NewlineValidator", "WhitespaceValidator"]):
- sys.exit(1)
- sys.exit(0)
- if __name__ == '__main__':
- main()
|