diff --git a/CHANGELOG.md b/CHANGELOG.md index 81d731a7..71e6f892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Status of the `main` branch. Changes prior to the next official version change w - Security: Add check for tool being read-only in the project server (previously only checked in `query_project` tool, i.e. client side) - Usage reporting now also includes the name of the Serena context that is used - Fix: restricted `insert_after_symbol` to raise if used on an assignment or similar (can't reliably determine the symbol range) + - Fix: Failure to collect project ignore spec now logs the error and downstream tasks fail fast, fixing hanging LS initialisation * JetBrains: - `Move` and `SafeDelete` tools: transform empty string to None (counteracts client errors) diff --git a/src/serena/project.py b/src/serena/project.py index c94d8b45..24b5948f 100644 --- a/src/serena/project.py +++ b/src/serena/project.py @@ -307,38 +307,42 @@ class Project(ToStringMixin): # prepare ignore spec asynchronously, ensuring immediate project activation. self.__ignored_patterns: list[str] - self.__ignore_spec: pathspec.PathSpec + self.__ignore_spec: pathspec.PathSpec | None self._ignore_spec_available = threading.Event() threading.Thread(name=f"gather-ignorespec[{self.project_config.project_name}]", target=self._gather_ignorespec, daemon=True).start() def _gather_ignorespec(self) -> None: with LogTime(f"Gathering ignore spec for project {self.project_config.project_name}", logger=log): - # gather ignored paths from the global configuration, project configuration, and gitignore files - global_ignored_paths = self.serena_config.ignored_paths - ignored_patterns = list(global_ignored_paths) + list(self.project_config.ignored_paths) - if len(global_ignored_paths) > 0: - log.info(f"Using {len(global_ignored_paths)} ignored paths from the global configuration.") - log.debug(f"Global ignored paths: {list(global_ignored_paths)}") - if len(self.project_config.ignored_paths) > 0: - log.info(f"Using {len(self.project_config.ignored_paths)} ignored paths from the project configuration.") - log.debug(f"Project ignored paths: {self.project_config.ignored_paths}") - log.debug(f"Combined ignored patterns: {ignored_patterns}") - if self.project_config.ignore_all_files_in_gitignore: - gitignore_parser = GitignoreParser(self.project_root) - for spec in gitignore_parser.get_ignore_specs(): - log.debug(f"Adding {len(spec.patterns)} patterns from {spec.file_path} to the ignored paths.") - ignored_patterns.extend(spec.patterns) - self.__ignored_patterns = ignored_patterns + try: + # gather ignored paths from the global configuration, project configuration, and gitignore files + global_ignored_paths = self.serena_config.ignored_paths + ignored_patterns = list(global_ignored_paths) + list(self.project_config.ignored_paths) + if len(global_ignored_paths) > 0: + log.info(f"Using {len(global_ignored_paths)} ignored paths from the global configuration.") + log.debug(f"Global ignored paths: {list(global_ignored_paths)}") + if len(self.project_config.ignored_paths) > 0: + log.info(f"Using {len(self.project_config.ignored_paths)} ignored paths from the project configuration.") + log.debug(f"Project ignored paths: {self.project_config.ignored_paths}") + log.debug(f"Combined ignored patterns: {ignored_patterns}") + if self.project_config.ignore_all_files_in_gitignore: + gitignore_parser = GitignoreParser(self.project_root) + for spec in gitignore_parser.get_ignore_specs(): + log.debug(f"Adding {len(spec.patterns)} patterns from {spec.file_path} to the ignored paths.") + ignored_patterns.extend(spec.patterns) + self.__ignored_patterns = ignored_patterns - # Set up the pathspec matcher for the ignored paths - # for all absolute paths in ignored_paths, convert them to relative paths - processed_patterns = [] - for pattern in ignored_patterns: - # Normalize separators (pathspec expects forward slashes) - pattern = pattern.replace(os.path.sep, "/") - processed_patterns.append(pattern) - log.debug(f"Processing {len(processed_patterns)} ignored paths") - self.__ignore_spec = pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, processed_patterns) + # Set up the pathspec matcher for the ignored paths + # for all absolute paths in ignored_paths, convert them to relative paths + processed_patterns = [] + for pattern in ignored_patterns: + # Normalize separators (pathspec expects forward slashes) + pattern = pattern.replace(os.path.sep, "/") + processed_patterns.append(pattern) + log.debug(f"Processing {len(processed_patterns)} ignored paths") + self.__ignore_spec = pathspec.PathSpec.from_lines(pathspec.patterns.GitWildMatchPattern, processed_patterns) + except Exception as e: + log.error(f"Error while gathering ignore spec for project {self.project_config.project_name}: {e}", exc_info=e) + self.__ignore_spec = None self._ignore_spec_available.set() @@ -401,6 +405,10 @@ class Project(ToStringMixin): log.info("Waiting for ignore spec to become available ...") self._ignore_spec_available.wait() log.info("Ignore spec is now available for project; proceeding") + if self.__ignore_spec is None: + raise ValueError( + "The ignore spec could not be computed; please check the log for errors and report the error: https://github.com/oraios/serena/issues" + ) return self.__ignore_spec @property