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
Add new EnableNewCopsUpTo
option to enable all pending cops up to a specific Rubocop version
#8565
base: master
Are you sure you want to change the base?
Conversation
I'm afraid the proposed solution doesn't work with extensions (as they also introduce cops as pending, but have different version numbers), which is why such a feature request was rejected originally. See this ticket for more details #8249 |
Ah I see, I missed that thread – that's my bad, sorry! I'd like to still discuss this a little more if that's ok, although if you consider this already discussed and decided and don't want to re-discuss it I understand :) Firstly I'll happily admit I don't know much about extensions, I haven't used them much, so I might be wrong on any point here and would be grateful for anyone with more experience chiming in! However my gut feeling is that, from a user perspective, even if you have to separately specify versions for each extension this would still be less work? I'm guessing that most extensions have more than one cop, so it would be fewer lines of configuration to enable pending cops via version than by writing each one out. Here's a real-world example from my current project of what the Add each pending cop (current scenario):
Version specifiers for each extension (proposed solution):
Before we get into the finer details of how such a feature might work technically I'd like to ask you what you think of this at a high level. Are my assumptions about extensions correct? Would this be less work for users? Is such an approach even feasible, given how extensions and versioning them works? |
I'm not fond of introducing a new configuration option, besides |
That's fair, we can stick with just I took a shot at coding up the same thing but with
I mostly like this. It still solves the problem, this is a lot less to keep up to date than a long list of enabled cops, and it doesn't matter that the gems aren't in sync on version numbers as they're all controlled separately. The only bit I'm not so keen on is having separate configuration for Is there a way to group those into one that makes sense? I'm worried about introducing any new concepts into the configuration, and that it might be confusing if most keys refer strictly to departments and one is referring to the gem the cops are from. |
I also want to mention that I've thought of some other potential solutions to this problem too. I'm not attached to solving the problem in the way I proposed above, I'm only attached to improving the ergonomics around pending cops :) So, I think it's maybe worth considering other approaches as well as refining the version-based solution? Option 2: Common version number between extensionsObviously extensions can't be kept strictly in sync version wise, but they could all have some shared version that is only bumped once in a while (yearly? quarterly?) e.g. "Version 2019" or similar. Like Rust's "epochs", a little. Upsides:
Downsides:
Option 3: Date-based enablingCops could have additional metadata for the date they were introduced, similar to the metadata they already have for which version they were introduced in. Users could then enable all cops introduced by a certain date. Upsides:
Downsides:
Option 4: Make the current status quo less workAdding every pending cop to your configuration file has two problems, as I see it: extra noise in the configuration file, and tedious work to add each one. I've found a nice solution to both problems in the meantime while working on this PR – I've separated the pending cops part of configuration into its own file, and written a small Rake task that hooks into Rubocop and appends a line for each pending cop to that file. Now it's very easy to keep up to date! Upsides:
Downsides:
Personally, I still like the idea of being able to set version numbers for |
@rubocop-hq/rubocop-core Any thoughts? For the protocol - I still like the most the idea to use a version per department/extension. |
From this issue and others (comes to my mind: #8490 and showing doc URL for example), it seems like there is a need to map a Cop class to a Corps (or Service?) which could provide meta information: unique identifier (like This could be done either via singleton class methods of I wish I had a more informed opinion. |
This approach looks good to me. It seems that setting |
I guess we'll pick up this conversation again after we cut 1.0. |
It seems like everyone is happy with the first approach, version per department/extension? If so I'm happy going ahead and turning my proof of concept into a decent patch. |
@zofrex Go for it! |
@zofrex Any progress on this front? |
@bbatsov I haven't had any time to spend on this since writing up the proof of concept, I'm afraid. I'm hoping to get to this in the next week or two, but if someone else wants to take it on then by all means go ahead, as I can't promise anything. I'll push the proof of concept in case it's useful for implementing this for reals, but I don't think the code is in great shape. By the way, I have noticed that it seems the logic for handling cops is duplicated: once for displaying pending cops on the CLI, and once for actually picking which cops run. I think with this amount of complexity being added to pending/enabled logic it would make sense to find a way to combine those, to avoid any disagreement between them which would be very confusing for users. I'm not sure how best to do that, though. |
No rush. I was just going over all open PRs today. |
Ok, cool. Do you have any thoughts on the duplication of the pending/enabled cops logic? Do you agree it's a problem, first of all? And if you do, do you have any thoughts on how to resolve it? |
@zofrex There's not much logic in how Pending cops are treated. By the way, WDYT about only allowing |
7aed226
to
19f67f7
Compare
I've written a first draft of doing this as agreed, per department/extension. It's missing one thing for sure, which is configuration validation – it wasn't clear to me how to add that for a per-department attribute. I'm sure there will be other things too!
@pirj this sounds like a good idea to me, what do you think the failure mode should be for this? A warning or an error? |
Seems we all missed the last update on this PR. We'll review it shortly. |
No worries, I was going to resolve the merge conflicts before bumping :) I'll do that now! |
19f67f7
to
7b9f279
Compare
context "when the department's NewCops option is a version > VersionAdded" do | ||
let(:department_option) { "NewCops: '1.81'" } | ||
|
||
it 'does not display a pending cop warning for that cop' do | ||
expect(output).to start_with(pending_cop_warning) | ||
expect(output).not_to include("Style/SomeCop: # new in 1.80\n Enabled: true") | ||
end | ||
end | ||
|
||
context "when the department's NewCops option is a version < VersionAdded" do | ||
let(:department_option) { "NewCops: '1.79'" } | ||
|
||
it 'does display a pending cop warning for that cop' do | ||
expect(output).to start_with(pending_cop_warning) | ||
expect(output).to include("Style/SomeCop: # new in 1.80\n Enabled: true") | ||
end | ||
end | ||
|
||
context "when the department's NewCops option is a version == VersionAdded" do | ||
let(:department_option) { "NewCops: '1.80'" } | ||
|
||
it 'does not display a pending cop warning for that cop' do | ||
expect(output).to start_with(pending_cop_warning) | ||
expect(output).not_to include("Style/SomeCop: # new in 1.80\n Enabled: true") | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updating these tests for Rubocop 1.0+ highlighted how fragile they are:
- They rely on the actual Rubocop version and will need to be changed when it passes the versions hardcoded here
- They rely on very specific output formatting from the CLI
- If anything updates underneath them (or a mistake is made when updating the tests), the "expect not_to include" ones have a tendency to pass, even if they shouldn't be, or aren't testing anything useful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They rely on the actual Rubocop version and will need to be changed when it passes the versions hardcoded here
You probably could stub out the version constant for the tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree though that these tests are brittle, it’d be good if we could generate a data structure of cops that will be outputted and use that both to output and to test against, rather than testing the output itself.
At the very least, instead of expect not_to include
I would test for the specific full message so that it’ll fail if something changes, but this might get overly verbose.
I’m not sure I love a per department setting because unlike the extensions which generally only have one, rubocop itself has a bunch of departments that would have to be configured separately and maintained. @marcandre’s “corps” idea might be a solution. I’m also wondering if there might be different approach where we keep track of the last version of each gem that was “updated” to (there’d have to be a mechanism for tracking this), like a Anyways, I haven’t looked at the code in depth yet but here are some edge cases to think of, how do we expect each of these behave? AllCops:
NewCops: 1.15
Lint:
NewCops: 1.19 AllCops:
NewCops: disable
Lint:
NewCops: 1.19 AllCops:
NewCops: 1.19
Lint:
NewCops: disable Lint:
NewCops: 1.10
Lint/Foo:
Enabled: pending (We probably should have an error if someone tries to mark a cop as pending in their own config, unrelated to this PR specifically) Lint:
NewCops: 2.0 # current version is 1.19 Lint:
NewCops: 0.0 # current version is 1.19 |
@@ -106,6 +106,8 @@ AllCops: | |||
# the `--enable-pending-cops` command-line option. | |||
# When `NewCops` is `disable`, pending cops are disabled in bulk. Can be overridden by | |||
# the `--disable-pending-cops` command-line option. | |||
# You can also set this to a version number per-department, e.g. `NewCops: 1.1` will | |||
# enable all cops that were added in version 1.1 and earlier of the relevant plugin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe "extension" is a more appropriate term.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that extensions and departments are orthogonal. An extension can add cops to a department that exists in the core RuboCop.
Making this as flexible as allowing to configure it per-extension and per department is an overkill. AllCops/NewCops
for RuboCop core and optional separate per-extension is just fine.
WDYT:
AllCops:
NewCops: 1.15
equivalent to:
AllCops:
NewCops:
rubocop: 1.15
For extensions:
AllCops:
NewCops:
rubocop: 1.15
rubocop-rails: 2.5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Setting a version number, is ambiguous.
Should a version of RuboCop core or an extension that introduced a certain cop to the existing department be used to decide if the cop is affirmed or not?
I believe some kind of warning should be issued in case of such an ambiguity, e.g.:
`Style/NewCops` is set to `1.15`. However, both `rubocop` and `rubocop-nitpicks` provide cops to the `Style` department, and their versions do not align.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a problem will be we don’t know what extension adds what cop but I agree per extension makes more sense to me than per dept.
Briefly skimmed through comments, and I think we've agreed that the approach with setting the version separately for extensions (and core) is the only choice. |
@zofrex Ping |
@pirj I like your proposed syntax for doing it by extensions, and the short-hand for the core cops. If other people are OK with that I'd be up for giving that a shot, although I think the prevailing assumption was that we don't have enough data for this currently? I think the next step would be to look into how readily available that data is. I'll try to get to that, but don't let this vague promise stop anyone else from taking a look. |
Ping @rubocop/rubocop-core |
I would like to modestly propose a new feature for enabling pending cops in bulk:
I realised while creating my
.rubocop.yml
for a new project, that what I wanted to do with pending cops wasn't to enable all of them (and potentially fail CI in the future due to new cops) but nor did I want to ignore pending cops available right now that I could ensure my code passes. I know I could add each one individually to.rubocop.yml
but that's a bit tedious and my configuration is quite short and I'd rather keep it that way.What occurred to me is that I'd like to enable all the cops that were available in the version of Rubocop I'm using, and not ones in newer versions. And thus this feature idea was born.
With this feature you can set the
EnableNewCopsUpTo
option in your.rubocop.yml
file:And all pending cops that were added in that version or earlier will be enabled, and any added in the future will give a warning just like they do presently.
When you see those warnings and have some time, you can try enabling them, fix any issues, and bump the version number in your config again.
I'm not married to any of the code here, but I'd rather bring a working demo along when proposing a feature. The version comparison code is maybe not great, it was just the fastest way to get a semver comparison to happen. I'm even less married to the option name which is quite clunky.
I added tests for the change in
registry.rb
but didn't really grok how to test the changes inconfig.rb
.I also added a first pass at documentation, but it could probably be clearer and will of course need to change if any of the names change.
I'm open to suggestions and feedback on any of the changes here! I thought this might be a neat way to deal with the problem and to have my cake and eat it, and based on reading some threads around here I have a feeling I'm not the only person who wants new cops but doesn't want a long list of enabled pending ones in their
.rubocop.yml
. Let me know what you think!Before submitting the PR make sure the following are checked:
[ ] Commit message starts with(N/A)[Fix #issue-number]
(if the related issue exists).master
(if not - rebase it).bundle exec rake default
. It executes all tests and RuboCop for itself, and generates the documentation.