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

Replace regex with Bundler::LockfileParser #12180

Merged
merged 1 commit into from Apr 5, 2024

Conversation

amomchilov
Copy link
Contributor

@amomchilov amomchilov commented Sep 2, 2023

Depends on #12186, see the difference: amomchilov/rubocop@add-requires_gem-api...parse-lockfile-with-bundler

Now that we have a Hash of all of the target's gems, we can just use that target_rails_version_from_bundler_lock_file, and remove the manual regex parsing.


Before submitting the PR make sure the following are checked:

  • The PR relates to only one subject with a clear title and description in grammatically correct, complete sentences.
  • 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.
  • Ran bundle exec rake default. It executes all tests and runs RuboCop on its own code.
  • Added an entry (file) to the changelog folder named {change_type}_{change_description}.md if the new code introduces user-observable changes. See changelog entry format for details.

@amomchilov amomchilov force-pushed the parse-lockfile-with-bundler branch 3 times, most recently from 6a4dbc9 to 2d52783 Compare September 2, 2023 20:52
@koic
Copy link
Member

koic commented Sep 3, 2023

Is the Bundler::LockfileParser a stable published API that can be used safely? I'm concerned about whether it's a private API intended for internal use within Bundler, which might introduce breaking changes in future Bundler versions.

def inspect # :nodoc:
"#<#{self.class.name}:#{object_id} @loaded_path=#{loaded_path}>"
end

private

def target_rails_version_from_bundler_lock_file
@target_rails_version_from_bundler_lock_file ||= read_rails_version_from_bundler_lock_file
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 removed this memoization because:

  1. If read_rails_version_from_bundler_lock_file returned nil, it wouldn't memoize correctly and would be trying to parse the lockfile on every call anyway
  2. This value is only accessed from #target_rails_version, which is itself memoized

@amomchilov
Copy link
Contributor Author

amomchilov commented Sep 4, 2023

Hey @koic!

I don't see any firm evidence that it's intended to be public (e.g. YARD docs), but I think it's very likely to be the case.

Package management is central to an ecosystem, and being able to introspect dependencies is pretty core to a bunch of tooling. If it's not public, we should perhaps try to make it public, like how Gem::Version is used throughout the ecosystem today.

Being dependant on Bundler::LockfileParser isn't well justified by this PR on its own, but I needed it for #12186. Once that's implemented, we can use it here for "free".

@koic
Copy link
Member

koic commented Sep 4, 2023

@deivid-rodriguez @hsbt May I ask the Bundler development team, Is Bundler::LockfileParser intended to be used as a published API? I'm concerned about potential incompatibilities arising from the use of private API.

@amomchilov amomchilov force-pushed the parse-lockfile-with-bundler branch 3 times, most recently from 145c615 to 4557ea0 Compare September 4, 2023 15:42
@deivid-rodriguez
Copy link
Contributor

Hei @koic. Bundler::LockfileParser is not documented anywhere as a public API, but I've seen many cases of people using it. So We don't plan to intentionally break things for people. That said, given it's not documented at the moment, it should still be "used at your own risk". We could probably formalize an API at some point.

@koic
Copy link
Member

koic commented Sep 25, 2023

@deivid-rodriguez I've come to understand the Bundler::LockfileParser API. Thank you very much!

@deivid-rodriguez
Copy link
Contributor

No problem!

@bbatsov
Copy link
Collaborator

bbatsov commented Sep 30, 2023

I don't mind using Bundler::LockfileParser. @amomchilov Let us know when you feel the PR is ready for review.

@amomchilov
Copy link
Contributor Author

amomchilov commented Sep 30, 2023

@bbatsov добър вечер!

I've just rebased it. This PR builds on top of #12186, which needs to be reviewed and shipped first. After that, I think this PR is also ready for review/merge (just look at the last commit)

@amomchilov amomchilov marked this pull request as ready for review September 30, 2023 15:59
@koic
Copy link
Member

koic commented Sep 30, 2023

The commits related to the changes in #12180 are mixed in. Can you leave the change for only "Replace regex with Bundler::LockfileParser"?

@koic
Copy link
Member

koic commented Sep 30, 2023

It just occurred to me that RuboCop doesn't have a runtime dependency on Bundler. This means to utilize Bundler's API, a runtime dependency on Bundler would be required. However, introducing such a dependency solely for the sake of using Bundler::LockfileParser seems like an overkill.

Perhaps continuing to resolve this with regexp would be a more appropriate approach. In other words, it might be better if this change were not made.

@amomchilov
Copy link
Contributor Author

The commits related to the changes in #12180 are mixed in.

Hey @koic, did you mean #12186? This "Replace regex with Bundler::LockfileParser" change depends on the code introduced in the other PR. The other PR would need to be merged first. Could you please take a look?

However, introducing such a dependency solely for the sake of using Bundler::LockfileParser seems like an overkill.

Perhaps continuing to resolve this with regexp would be a more appropriate approach. In other words, it might be better if this change were not made.

@koic For this change on its own, I would agree. But the real benefit here is new requires_gem feature I've proposed and implemented in #12186. Once that PR introduces the ability to parse the lockfile, this PR just becomes a natural next step: to replace this manual lockfile parsing with the results we've already got from #12186.

@bbatsov
Copy link
Collaborator

bbatsov commented Oct 5, 2023

I agree it's not prudent to add a runtime dependency on Bundler, so I see three paths ahead:

  • Using this parser only when available
  • Inlining something similar into RuboCop (I'm not sure how complex its implementation is)
  • Keeping things as they are

@amomchilov
Copy link
Contributor Author

@bbatsov You raise an interesting point, I'll think on it!

In the mean time, could I get your thoughts on this feature request and the implementation I proposed for it?

Is the requires_gem API useful enough to justify the bundler dependency? Or perhaps I should extract it into a module that you mix into your cops as-needed, and then make the bundler dependency only required for that.

@amomchilov
Copy link
Contributor Author

amomchilov commented Oct 7, 2023

Just ran into this, apparently this is already used by RuboCop

def parser
return unless defined?(Bundler) && Bundler.default_lockfile
return @parser if defined?(@parser)
lockfile = Bundler.read_file(Bundler.default_lockfile)
@parser = lockfile ? Bundler::LockfileParser.new(lockfile) : nil
rescue Bundler::BundlerError
nil
end

@bbatsov
Copy link
Collaborator

bbatsov commented Oct 11, 2023

Ah, excellent - so I guess you know how to proceed now. :-)

@bbatsov
Copy link
Collaborator

bbatsov commented Nov 9, 2023

@amomchilov Any updates here?

@amomchilov
Copy link
Contributor Author

amomchilov commented Nov 9, 2023

Hey @bbatsov I had a question about how to proceed. Imessaged you in the RuboCop discord (I figured that was a good spot, because it showed you as online). Cross posting:


Добър вечер @bbatsov !

I'm taking a look at using the RuboCop::Lockfile abstraction that already exists in the codebase. It has its own logic to find the relevant lockfile for the repo (Bundler.default_lockfile) rather than the custom logic that exists in RuboCop::Config#bundler_lock_file_path.

They both do some directory traversal to find a gemfile in a parent dir, if it's not found in the cwd. Not sure if they're exactly the same, though 🤔

I'm not sure how to proceed. Do you think I should modify RuboCop::Lockfile to take in the path as a param (as determined by the existing RuboCopConfig#bundler_lock_file_path logic), or should I just use whatever RuboCop::Lockfile does already?

@bbatsov
Copy link
Collaborator

bbatsov commented Feb 28, 2024

I'm not sure how to proceed. Do you think I should modify RuboCop::Lockfile to take in the path as a param (as determined by the existing RuboCopConfig#bundler_lock_file_path logic), or should I just use whatever RuboCop::Lockfile does already?

I'd be fine with both approaches, but the first one sounds slightly better to me.

@amomchilov
Copy link
Contributor Author

@bbatsov Can we move the lock-file parsing related discussion to the underlying PR? That's what introduces that logic, it's an impl detail separate from the changes in this PR.

I responded here: #12186 (review)

@amomchilov amomchilov force-pushed the parse-lockfile-with-bundler branch 3 times, most recently from 3eaa47d to f518980 Compare March 5, 2024 02:14
@bbatsov
Copy link
Collaborator

bbatsov commented Apr 2, 2024

I guess you can rebase this one now that #12186 is finally merged.

@amomchilov
Copy link
Contributor Author

@bbatsov Rebased, and ready to merge!

Thanks for jumping in to merge all these pieces :)

@bbatsov bbatsov merged commit b370532 into rubocop:master Apr 5, 2024
33 checks passed
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

4 participants