Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't load configuration files outside of the current project unless there's no project specific configuration #8314

Merged

Conversation

deivid-rodriguez
Copy link
Contributor

@deivid-rodriguez deivid-rodriguez commented Jul 12, 2020

In previous PRs, #8176 and #8239, I fixed particular cases of this problem, but it was still not fixed in general. So, if I have my personal configuration at ~/.config/rubocop/config.yml and my project is at ~/Code/my_project, then my personal configuration won't be loaded thanks to those PRs. However, if I have my personal configuration at ~/.rubocop.yml, then rubocop will still load exclusions from there because $HOME is an ancestor of the current project folder, and that's how find_last_file_upwards currently works.

To fix this problem in general, I made sure find_last_file_upwards (and any configuration search in general) doesn't go past the current project's root. To do that, I needed to define what the root of a project is. While there were other options, like presence of a .git folder, for example, I decided go with "the highest folder up in the directory hierarchy containing a Gemfile or gems.rb" file.

This is the same definition bundler uses and I believe it makes sense, specially because the original problems I want to solve here manifest were personal configuration files try to load rubocop plugins outside of the current bundle, and thus making rubocop crash. See #7433. By using Gemfile presence as a criteria, we prevent exactly this kind of problem.

As a note, the reason the test suite never catched this issue is that it intentionally defines a not very common folder structure where the current project folder is not inside $HOME. I think that maybe the structure was defined this way in order to workaround this problem.

Finally, although this should work for most ruby projects out there, projects without a Gemfile don't fit into this criteria. I believe we can leave it as it is tough, and wait for issues from such projects before adding more fallbacks to this "root of a project" definition.

I'd be very happy to hear your thoughts, thanks for reading!

  • Wrote good commit messages.
  • Commit message starts with [Fix #issue-number] (if the related issue exists).
  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Added an entry to the Changelog if the new code introduces user-observable changes. See changelog entry format.
  • The PR relates to only one subject with a clear title and description in grammatically correct, complete sentences.
  • Run bundle exec rake default. It executes all tests and RuboCop for itself, and generates the documentation.

Previously, the meaning of `FileFinder.root_level` would be "first non
searched directory", now it is "last searched directory".

This is more intuitive in my opinion (I assumed this behavior from the
beginning until I found out it was not like that), but also more
"correct", because it allows you to search up to the root directory if
`FileFinder.root_level` is set to "/".

Note that this `FileFinder.root_level?` logic is only useful for testing to
provide an isolated environment not affected by the external file system
where the tests are running. When rubocop runs "normally", it's ignored.

However, in follow up work, I plan to start using these code paths, and
this change allows me to stop artificially passing the parent of the
directory where I want to stop searching.
@deivid-rodriguez deivid-rodriguez force-pushed the full_fix_for_personal_configuration branch from f58c72a to 1d3b65f Compare July 13, 2020 09:08
If there's a specific project configuration, we don't want to pick up
the exclusions in the user configuration. This doesn't happen already in
the case of XDG setups, or if the HOME directory is not a parent of the
project root directory. The specs currently configure such a setup, even
if not very common in real life, maybe to workaround this issue.

This PR fixes the problem by defining the root of project as the highest
folder up in the directory hierarchy that contains a `Gemfile` or
`gems.rb` file. Even if this definition matches most ruby projects, we
could add additional fallbacks (like a `.gemspec` file?) to include more
edge cases.
If by any chance, one happens to leave a `.rubocop.yml` file outside of
my project, ignore it.
@deivid-rodriguez deivid-rodriguez force-pushed the full_fix_for_personal_configuration branch from 1d3b65f to 469ca61 Compare July 13, 2020 09:17
@@ -24,7 +24,7 @@ class << self

attr_accessor :debug, :ignore_parent_exclusion,
:disable_pending_cops, :enable_pending_cops
attr_writer :default_configuration
attr_writer :default_configuration, :project_root
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only for test code to be able to reset state, the project_root doesn't really change at runtime. I can alternatively revert this change and use instance_variable_set inside the test code to reset it.

def project_root
@project_root ||= find_project_root
end

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I intentionally left this method above in the public section and documented it since I believe it could be useful for extensions. At least it's useful for a cop I'm helping with in the rubocop-packaging extension.

@bbatsov bbatsov merged commit 535c544 into rubocop:master Jul 13, 2020
@bbatsov
Copy link
Collaborator

bbatsov commented Jul 13, 2020

Nicely done!

Finally, although this should work for most ruby projects out there, projects without a Gemfile don't fit into this criteria. I believe we can leave it as it is tough, and wait for issues from such projects before adding more fallbacks to this "root of a project" definition.

Probably .gemspec should be used as a root marker as well, but that's not a big deal.

Might be a good idea to add a few lines in the docs about not adding exclusions to the global config file, or potentially adding some validation and warnings when this is done.

@deivid-rodriguez deivid-rodriguez deleted the full_fix_for_personal_configuration branch July 13, 2020 12:24
@deivid-rodriguez
Copy link
Contributor Author

Thanks @bbatsov! 💜

Probably .gemspec should be used as a root marker as well, but that's not a big deal.

I can prepare a followup PR to add this 👍.

Might be a good idea to add a few lines in the docs about not adding exclusions to the global config file, or potentially adding some validation and warnings when this is done.

I think there might be some situations where exclusions in the global configuration file make sense. Like if you have a lot of projects with a tempdir folder (or something else not included in rubocop default exclusions), some of them configured to use rubocop and some where rubocop is not used. For those where rubocop is not used, you might want to add this exclusion to your global (personal) configuration, so that they are picked up there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants