예제 #1
0
    def get_staged_files_paths(self):
        """
        Gets the relative paths of all staged files.

        Returns:
            A list with the relative paths of all files that are currently
            staged.
        """
        head_hash = self.get_head_hash()

        with exec_in_dir(self.root):
            pipe = Popen(["git", "diff", "--cached", "--name-only", head_hash],
                         stdout=PIPE,
                         stderr=PIPE)
            out, err = pipe.communicate()

            staged_files = (
                out.decode('utf-8')[:-1]  # remove trailing '\n'
                .split('\n'))

            # drop nulls produce by split, if any
            staged_files = [file for file in staged_files if file]

            staged_files_paths = [
                path.relpath(file, self.root) for file in staged_files
            ]

            return staged_files_paths
예제 #2
0
 def __init__(self, path=None, *args, **kwargs):
     """
     Args:
         path: an absolute path to the root or a subdirectory of a git
             repository.
     """
     # get repository root
     with exec_in_dir(path):
         self.root = self._get_git_root()
예제 #3
0
    def test_no_dir_change(self):
        # write a test file in current directory and check its path

        try:
            # test
            with exec_in_dir(None):
                test_file_path = path.join(self.cwd, "foo.txt")
                with open(test_file_path, "w") as foo:
                    foo.write("a-ha!")

            self.assertEqual(path.abspath(getcwd()), path.abspath(self.cwd))
            self.assertTrue(path.isfile(path.abspath(test_file_path)))

        except Exception:
            raise

        finally:
            # clean up
            remove(path.abspath(test_file_path))
예제 #4
0
    def get_staged_file_content(self, staged_file_path):
        """
        Gets the contents of a given staged file.

        Args:
            staged_file_path: the path of a file that is currently staged.

        Returns:
            A byte literal corresponding to the contents of the staged file.
        """
        # quote the staged_file name or path in order to take care of
        # escaping in the shell
        with exec_in_dir(self.root):
            pipe = Popen(
                ["git", "show", ":%s" % staged_file_path],
                stdout=PIPE,
                stderr=PIPE)
            out, err = pipe.communicate()

            return out
예제 #5
0
    def get_head_hash(self):
        """
        Gets the current HEAD's hash.

        Returns:
            A string with the current HEAD's hash.
        """
        with exec_in_dir(self.root):
            pipe = Popen(["git", "rev-parse", "--verify", "HEAD"],
                         stdout=PIPE,
                         stderr=PIPE)
            out, err = pipe.communicate()

            # strip the trailing '\n'
            head_hash = out.decode('utf-8')[:-1]

            # use the special hash if there is no HEAD
            if not head_hash:
                head_hash = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

            return head_hash
예제 #6
0
    def test_dir_change(self):
        # write a test file in a subdirectory and check its path

        try:
            # make subdirectory
            subdir = path.join(self.cwd, "foo_subdir")
            mkdir(subdir)

            # test
            with exec_in_dir(subdir):
                test_file_path = path.join(getcwd(), "foo_sub.txt")
                with open(test_file_path, "w") as foo:
                    foo.write("a-ha!")

            self.assertEqual(path.abspath(getcwd()), path.abspath(self.cwd))
            self.assertTrue(path.isfile(path.abspath(test_file_path)))

        except Exception:
            raise

        finally:
            # clean up
            rmtree(subdir)
예제 #7
0
    def run(self):
        """
        Main method that executes all of the available linters.

        Returns:
            An integer corresponding to the number of staged files with
            linting problems.
        """
        try:
            # get staged files
            staged_files_paths = self.git_handle.get_staged_files_paths()

            # check that paths and file names are ok
            for _path in staged_files_paths:
                self.git_handle._check_path_is_allowed(_path)

            # create a temporary directory
            tmp_dir = TemporaryDirectory()

            # write the content of the staged files to temporary files
            files_in_tmp_dir = []   # list for rel paths of temporary files
            for rel_path in staged_files_paths:
                # ensure parent directory of a staged file exists inside of
                # `tmp_dir`
                makedirs(path.join(
                    tmp_dir.name,
                    path.dirname(rel_path)
                ), exist_ok=True)
                # write content of staged file to a temporary file and
                # collect relative path in the list
                tmp_file_path = path.join(tmp_dir.name, rel_path)
                with open(tmp_file_path, "wb") as tmp_file:
                    content = self.git_handle.get_staged_file_content(rel_path)
                    tmp_file.write(content)
                    files_in_tmp_dir.append(
                        path.relpath(tmp_file_path, tmp_dir.name)
                    )

            # get current directory and change directory to temporary directory
            # (this is to ensure that relative paths are correctly displayed
            # during linting and that linters run on the staged version of the
            # files, which are the ones saved in the temporary files);
            # not changing directory can cause the paths to be interpreted
            # relatively to the git repository root, which can cause the
            # linters to run on the version of the files that is currently
            # in the tree!
            with exec_in_dir(tmp_dir.name):
                # initialize a counter to count how many linters return a
                # non-zero exit status
                non_zero_linters = 0
                for linter in self.linters:
                    # run the linters
                    non_zero_linters += linter.lint(files_in_tmp_dir)

                return non_zero_linters

        except Exception:
            raise

        finally:
            tmp_dir.cleanup()