diff --git a/docs/configuration.rst b/docs/configuration.rst index 967d5cb30..84a5b3bd2 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -57,6 +57,9 @@ provided using CLI, Robocop will try to find default configuration file using th - otherwise, if the directory contains ``pyproject.toml`` file, load it - otherwise, go to parent directory. Stop search if ``.git`` or top disk directory is found +It is possible to not stop searching parent directories even if ``.git`` directory is found using +``--ignore-git-dir`` flag. + ``.robocop`` argument file -------------------------- diff --git a/docs/releasenotes/unreleased/other.2.rst b/docs/releasenotes/unreleased/other.2.rst new file mode 100644 index 000000000..dc75b1598 --- /dev/null +++ b/docs/releasenotes/unreleased/other.2.rst @@ -0,0 +1,5 @@ + --ignore-git-dir option to ignore .git when searching for configuration file (#908) +------------------------------------------------------------------------------------ + +When searching for the default configuration file, Robocop stop searching if ``.git`` directory is found. It is now +possible to disable this behaviour using ``--ignore-git-dir`` flag. diff --git a/robocop/config.py b/robocop/config.py index 281d5d5fb..e199ce814 100644 --- a/robocop/config.py +++ b/robocop/config.py @@ -201,6 +201,7 @@ def __init__(self, root=None, from_cli: bool = False): self.recursive = True self.verbose = False self.persistent = False + self.ignore_git_dir = False self.config_from = "" self.root = find_project_root(root, ["."]) self.parse() @@ -374,6 +375,13 @@ def _create_parser(self): ) optional.add_argument("-A", "--argumentfile", metavar="PATH", help="Path to file with arguments.") optional.add_argument("--config", metavar="PATH", help="Path to TOML configuration file.") + optional.add_argument( + "--ignore-git-dir", + action="store_true", + default=self.ignore_git_dir, + help="Use this flag to continue searching for the default configuration file even if parent directory " + "contains '.git' directory. Useful for multirepo.", + ) optional.add_argument( "-g", "--ignore", @@ -435,7 +443,7 @@ def parse(self): return args = sys.argv[1:] if not self.config_file_in_cli(args): - self.load_default_config_file() + self.load_default_config_file(ignore_git_dir="--ignore-git-dir" in args) self.parse_args(args) @staticmethod @@ -459,20 +467,20 @@ def print_config_source(self): else: print("No config file found or configuration is empty. Using default configuration") - def load_default_config_file(self): + def load_default_config_file(self, ignore_git_dir: bool = False): """Find and load default configuration file. First look for .robocop file. If it does not exist, search for pyproject.toml file.""" - if self.load_robocop_file(): + if self.load_robocop_file(ignore_git_dir): return - pyproject_path = find_file_in_project_root("pyproject.toml", self.root) - if pyproject_path.is_file(): + pyproject_path = find_file_in_project_root("pyproject.toml", self.root, ignore_git_dir) + if pyproject_path is not None: self.load_pyproject_file(pyproject_path) - def load_robocop_file(self): + def load_robocop_file(self, ignore_git_dir: bool): """Returns True if .robocop exists""" - robocop_path = find_file_in_project_root(".robocop", self.root) - if not robocop_path.is_file(): + robocop_path = find_file_in_project_root(".robocop", self.root, ignore_git_dir) + if robocop_path is None: return False argument_files_parser = ArgumentFileParser() args = argument_files_parser.load_argument_file(robocop_path, robocop_path.parent) diff --git a/robocop/files.py b/robocop/files.py index 853a9ef40..8d3084063 100644 --- a/robocop/files.py +++ b/robocop/files.py @@ -41,11 +41,12 @@ def find_project_root(root, srcs): return directory -def find_file_in_project_root(config_name, root): +def find_file_in_project_root(config_name, root, ignore_git_dir: bool): for parent in (root, *root.parents): - if (parent / ".git").exists() or (parent / config_name).is_file(): + if (parent / config_name).is_file(): return parent / config_name - return parent / config_name + if not ignore_git_dir and (parent / ".git").exists(): + return None @lru_cache() diff --git a/tests/test_data/default_config_outside_git/pyproject.toml b/tests/test_data/default_config_outside_git/pyproject.toml new file mode 100644 index 000000000..e69de29bb diff --git a/tests/utest/test_configuration_file.py b/tests/utest/test_configuration_file.py index 799b9d7ee..d71776bd5 100644 --- a/tests/utest/test_configuration_file.py +++ b/tests/utest/test_configuration_file.py @@ -31,14 +31,25 @@ class TestConfigurationFile: def test_find_project_root_same_dir(self, path_to_test_data): src = path_to_test_data / "default_config" with working_directory(src): - root = find_file_in_project_root(".robocop", src) + root = find_file_in_project_root(".robocop", src, False) assert root == src / ".robocop" def test_find_project_root_missing_but_git(self, path_to_test_data): src = path_to_test_data / "default_config_missing" / "nested" / "deeper" with working_directory(src): - root = find_file_in_project_root(".robocop", src) - assert root == Path(__file__).parent.parent.parent / ".robocop" + root = find_file_in_project_root(".robocop", src, False) + assert root is None + + def test_ignore_git_dir(self, path_to_test_data): + src = path_to_test_data / "default_config_outside_git" + cwd_src = src / "root" + (cwd_src / ".git").mkdir(parents=True, exist_ok=True) + with working_directory(cwd_src): + config_file_disabled = find_file_in_project_root("pyproject.toml", cwd_src, False) + config_file_enabled = find_file_in_project_root("pyproject.toml", cwd_src, True) + + assert config_file_disabled is None + assert config_file_enabled == src / "pyproject.toml" def test_load_config_from_default_file(self, path_to_test_data): src = path_to_test_data / "default_config"