astral-sh / ruff

An extremely fast Python linter and code formatter, written in Rust.
https://docs.astral.sh/ruff
MIT License
31.05k stars 1.03k forks source link

Feature: Extend ruff configuration from external file #12352

Open yeoffrey opened 2 months ago

yeoffrey commented 2 months ago

TD;LR

Enable the ability to import ruff.toml configurations from external sources such as URLs or git repositories.

✅ I am willing to contribute to make this feature.

Use Case

In our own projects, we maintain multiple repositories. When we need to update our linting rules, we currently have to modify the ruff.toml file in each project individually. This process is time-consuming and prone to inconsistencies. It would be beneficial to centralize these configurations, allowing us to update rules in a single location and propagate them across all projects.

For our Javascript projects, we have a shared eslint configuration from a repository that is imported via a project dependency. When we make an update to the configuration, the dependency will update. It saves us a lot of time, and I think Ruff should have a similar solution available for this case.

Proposed Solution

Introduce support for remote imports in ruff.toml. This feature would allow users to specify an external URL or git repository from which the ruff.toml configuration can be fetched and extended. This could be implemented either as a Python dependency or directly in the ruff.toml file.

Example Configuration

# ruff.toml
[import]
url = "https://example.com/path/to/ruff.toml"

# or for git repository
[import]
git = "https://github.com/organization/repo"
branch = "main"
path = "path/to/ruff.toml"

# Local overrides
[tool.ruff]
line-length = 88

Conclusion

This feature would streamline the process of sharing and updating linter configurations across multiple projects, enhancing maintainability and consistency. Centralizing configuration management would save time and reduce errors. Thank you for your continued efforts and a brilliant tool 🚀

Avasam commented 2 months ago

https://github.com/astral-sh/ruff/discussions/3363#discussioncomment-7266932

If/when this gets implemented in Ruff, I'd ask to consider a system that allows downloading the configs at install rather than at runtime to avoid issues with pre-commit.ci restrictions. (like dprint is having, see https://github.com/dprint/dprint/issues/442 )

MichaReiser commented 2 months ago

Thanks @yeoffrey for opening this issue and the kind words.

The first step for adding preset support is to create a design proposal and consensus behind it.

yeoffrey commented 2 months ago

If the configuration needs to be available at install time and not fetching at runtime, then I think the best option would be to create a preset as a development dependency that is installed with all of the other python packages. Inside of the ruff configuration file, you could point to a dependency that exports a configuration. I see this being a way to solve the question of caching, avoiding HTTP downloads during file traversal, ensuring reproducible builds. It also just leverages python's existing package system, so it doesn't feel unintuitive to handle it this way.

pip install my-ruff-config
# ruff.toml
[preset]
pkg = "my-ruff-config"

The only issue I see though, is how to export a toml file out of a python package:

  • How does it interact with extend (or should it be extend)
  • Can presets be mixed?
  • How does extending a preset work?

I think presets should be able to be extended, but based on the documentation I don't think extend will work for our needs since it only is extending other pyproject.toml files, and that still requires an answer as to whether than can be a dependency.

To make it work there should be an order of priority: 1) Normal ruff priorities as described here. 2) If a dependency is marked in a config file, then it should be overridden by local file configurations, and if those are overridden by other files then it shouldn't behave any differently.

These are just my basic thoughts right now, I might have others later.

MichaReiser commented 2 months ago

I don't think extend will work for our needs since it only is extending other pyproject.toml files

I'm not sure that i understand what you mean by that. extend supports extending any ruff.toml, .ruff.toml or a pyproject.toml.

I like using Python packages. The main question is how to find the Python package. More specifically. How do we know where the venv is (if any)

Avasam commented 2 months ago

I like using Python packages. The main question is how to find the Python package. More specifically. How do we know where the venv is (if any)

For projects like eslint and typescript it's easy: whatever environment they're run from so they can just import.

pyright is a non-python tool that needs to be aware of installed python packages, maybe you could take some inspiration from how it's doing it? Eric T. is generally a good reference for such standards too.

I see what looks like static typing efforts in Ruff (that red-knot thing?). Surely logic to statically find installed packages can be shared.