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

Appraisal doesn't support eval_gemfile #154

Open
glebm opened this issue May 19, 2019 · 8 comments
Open

Appraisal doesn't support eval_gemfile #154

glebm opened this issue May 19, 2019 · 8 comments

Comments

@glebm
Copy link

glebm commented May 19, 2019

eval_gemfile is not supported by appraisal.

Error:

appraisal-2.2.0/lib/appraisal/gemfile.rb:30:in `run':
undefined method `eval_gemfile' for #<Appraisal::Gemfile:0x000055dfb5f5bb18> (NoMethodError)

eval_gemfile is useful for sharing dependencies between multiple Gemfiles.

E.g. I have a rubocop.gemfile that's included into the main Gemfile but also into a separate rubocop-only Gemfile for running as a separate task on CI.

@glebm
Copy link
Author

glebm commented May 20, 2019

Hacky workaround:

# Put this in Gemfile:
require 'appraisal/bundler_dsl'
::Appraisal::BundlerDSL.class_eval do
  def eval_gemfile(path, contents = nil)
    (@eval_gemfile ||= []) << [path, contents]
  end

  private

  def eval_gemfile_entry
    @eval_gemfile.map { |(p, c)| "eval_gemfile(#{p.inspect}#{", #{c.inspect}" if c})" } * "\n\n"
  end

  alias_method :eval_gemfile_entry_for_dup, :eval_gemfile_entry

  self::PARTS << 'eval_gemfile'
end unless ::Appraisal::BundlerDSL::PARTS[-1] == 'eval_gemfile'

Proper support for eval_gemfile should also convert absolute paths back to File.expand_path(..., __dir__) calls in the output.

Decided against using Appraisal in this case though, as there doesn't seem to be any option for configuring the gemfiles directory path.

@nickcharlton
Copy link
Member

Decided against using Appraisal in this case though, as there doesn't seem to be any option for configuring the gemfiles directory path.

Would you be able to open an issue for this bit specifically?

@andreaswachowski
Copy link

Just FYI, this is still valid. I stumbled upon it with neovim and Shopify/ruby-lsp in a project that uses appraisal (I don't have control over that and cannot remove appraisal). ruby-lsp injects itself with a .ruby-lsp/Gemfile that looks like this:

# This custom gemfile is automatically generated by the Ruby LSP.
# It should be automatically git ignored, but in any case: do not commit it to your repository.

eval_gemfile(File.expand_path("../Gemfile", __dir__))
gem "ruby-lsp", require: false, group: :development
gem "ruby-lsp-rails", require: false, group: :development

In particular, thus there's eval_gemfile, see in https://github.com/Shopify/ruby-lsp/blob/5d59a71ffba325f56424afdac6462a4e7387e849/lib/ruby_lsp/setup_bundler.rb#L134 .

The Ruby language server consequently crashes ("Client ruby_ls quit with exit code 127 and signal 0). ~/.local/state/nvim/lsp.log contains

Ruby LSP Rails failed to initialize server: /path/to/rails/project/ ruby-lsp/Gemfile:4:in run': undefined method eval_gemfile' for #<Appraisal::Gemfile:0x000000011ea6df28 @sources=[], ...

@pboling
Copy link
Contributor

pboling commented Jan 4, 2025

Hoping to see eval_gemfile supported. It has become a useful tool to put the internal gems which are sourced from a private server into a separate Gemfile, and then eval that file in the various Gemfiles I have. Being able to use it within Appraisal definitions would be the final hurdle.

@pboling
Copy link
Contributor

pboling commented Jan 4, 2025

An updated hack, based on @glebm 's work above, which I got working by putting one copy in the root of the project, and another in the gemfiles/ directory (due to the above mentioned lack of a reliable relative path across invocations):

require "appraisal/bundler_dsl"

if ::Appraisal::BundlerDSL::PARTS.include?("eval_gemfile")
  # When the hack is already active, this code path is run within appraisal,
  #   so we can't really tell the difference between already hacked,
  #   and new version of appraisal has added the feature
else
  require "appraisal/appraisal"

  Appraisal::Appraisal.class_eval do
    def eval_gemfile(*args)
      gemfile.eval_gemfile(*args)
    end
  end

  Appraisal::BundlerDSL.class_eval do
    def eval_gemfile(path, contents = nil)
      (@eval_gemfile ||= []) << [path, contents]
    end

    private

    def eval_gemfile_entry
      @eval_gemfile.map { |(p, c)| "eval_gemfile(#{p.inspect}#{", #{c.inspect}" if c})" } * "\n\n"
    end

    alias_method :eval_gemfile_entry_for_dup, :eval_gemfile_entry

    self::PARTS << "eval_gemfile"
  end
end

NOTES:

  • when running a cold setup in CI you have to gem install appraisal before doing any normal bundle or appraisal commands, because you can't hack the appraisal gem unless it is already installed.

@pboling
Copy link
Contributor

pboling commented Jan 14, 2025

I implemented the pattern above, and it is working perfectly, see:
VitalConnectInc/turbo_tests@53b22cd

@nickcharlton
Copy link
Member

Nice, thanks!

Do you think you could open a PR to add this? We can pick it up from there then!

@pboling
Copy link
Contributor

pboling commented Jan 14, 2025

I'd love to! I'll work on that this week.

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

No branches or pull requests

4 participants