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

using spring does not work from subdirectories #644

Open
ccutrer opened this issue Apr 5, 2021 · 11 comments
Open

using spring does not work from subdirectories #644

ccutrer opened this issue Apr 5, 2021 · 11 comments

Comments

@ccutrer
Copy link
Contributor

ccutrer commented Apr 5, 2021

having a rails project with a spec directory:

spring rspec lib/acts_as_list_spec.rb
>> Spring was unable to find your config/application.rb file. Your project root was detected at /Users/cody/src/canvas-lms/spec, so Spring looked for /Users/cody/src/canvas-lms/spec/config/application.rb but it doesn't exist. You can configure the root of your application by setting Spring.application_root in config/spring.rb.

But non-springified works fine, properly searching the parent dir for the Gemfile, and then booting the app.

I've explored this quite a bit by monkey patching, and even if you can get the Spring client to boot via enough tricks, the server will still try to launch from the wrong directory and throw the same error. If you're sneaky and boot the server from the actual project root, and then run spring rspec from the spec directory, it will not have the some working directory, and just be confusing. It seems like the server needs to be directory aware - having the client pass its working directory through, and the server changing to that before running the command.

@ccutrer
Copy link
Contributor Author

ccutrer commented Apr 5, 2021

Note that getting this working would almost certainly open avenues for proper spring usage from the dummy app of an engine, or the reverse case - from within an engine embedded in a larger app, or the spring binstub being a symlink (again, from an engine embedded in a larger app)

@deivid-rodriguez
Copy link
Contributor

This could be the regression in 2.1.1. Can you try either the latest code in the master branch, or 2.1.0?

@JuPlutonic
Copy link

JuPlutonic commented Apr 7, 2021

Same error using spring (2.1.0):

spring rspec ...
/home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/soap4r-ruby1.9-2.0.5/lib/soap/mapping/encodedregistry.rb:150: warning: constant ::Fixnum is deprecated
/home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/soap4r-ruby1.9-2.0.5/lib/soap/mapping/encodedregistry.rb:216: warning: constant ::Fixnum is deprecated
/home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.3.6/lib/rails/application/configuration.rb:241:in `database_configuration': Cannot load database configuration:
Could not load database configuration. No such file - ["config/database.yml"] (RuntimeError)
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/railtie.rb:200:in `block (2 levels) in '
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/lazy_load_hooks.rb:71:in `class_eval'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/lazy_load_hooks.rb:71:in `block in execute_hook'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/lazy_load_hooks.rb:61:in `with_execution_control'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/lazy_load_hooks.rb:66:in `execute_hook'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/lazy_load_hooks.rb:43:in `block in on_load'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/lazy_load_hooks.rb:42:in `each'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/lazy_load_hooks.rb:42:in `on_load'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activerecord-6.0.3.6/lib/active_record/railtie.rb:198:in `block in '
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.3.6/lib/rails/initializable.rb:32:in `instance_exec'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.3.6/lib/rails/initializable.rb:32:in `run'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.3.6/lib/rails/initializable.rb:61:in `block in run_initializers'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:228:in `block in tsort_each'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:431:in `each_strongly_connected_component_from'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:349:in `block in each_strongly_connected_component'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:347:in `each'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:347:in `call'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:347:in `each_strongly_connected_component'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:226:in `tsort_each'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/2.6.0/tsort.rb:205:in `tsort_each'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.3.6/lib/rails/initializable.rb:60:in `run_initializers'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.3.6/lib/rails/application.rb:363:in `initialize!'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.3.6/lib/rails/railtie.rb:190:in `public_send'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/railties-6.0.3.6/lib/rails/railtie.rb:190:in `method_missing'
	from /home/X/canvas-lms/config/environment.rb:29:in `'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/dependencies.rb:324:in `require'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/dependencies.rb:324:in `block in require'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/dependencies.rb:291:in `load_dependency'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/activesupport-6.0.3.6/lib/active_support/dependencies.rb:324:in `require'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:106:in `preload'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:157:in `serve'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:145:in `block in run'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:139:in `loop'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:139:in `run'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application/boot.rb:19:in `'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:85:in `require'
	from /home/X/dotfiles/common/.anyenv/envs/rbenv/versions/2.6.6/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:85:in `require'
	from -e:1:in `'

@chriscz
Copy link

chriscz commented Apr 7, 2021

I ran a git bisect against spring (v2.0.0 to master) with a new rails 6.1 app but I was unable to find a good commit where the error message doesn't come up.

Here's the test code: https://github.com/chriscz/spring-nested-bug

@JuPlutonic
Copy link

I did installation of spring.2.1.1 master
And with it (pkg/spring-2.1.1.gem) inside canvas-lms application also got:
Could not load database configuration. No such file - ["config/database.yml"] (RuntimeError)

@deivid-rodriguez
Copy link
Contributor

Then that's not the culprit :(. The repro posted should be very helpful for anyone wanting to investigate the issue 👍.

@chriscz
Copy link

chriscz commented Apr 8, 2021

The bug is in the spring/configuration.rb code that uses gemfile which returns an absolute path to the Gemfile (at least in Bundler 2.2.14). Joining an absolute path with any other path always returns the absolute path and so the exists? check succeeds.

def find_project_root(current_dir)
if current_dir.join(gemfile).exist?
current_dir
elsif current_dir.root?
raise UnknownProject.new(Dir.pwd)
else
find_project_root(current_dir.parent)
end
end

I'm not entirely sure what the best fix would be, but maybe one of the following:

  1. checking whether the Gemfile is absolute and checking that config/application.rb exists and returning that directory?
    def find_project_root(current_dir)
      if gemfile.absolute? && gemfile.dirname.join('config/application.rb').exist?
        return gemfile.dirname
      end

      if current_dir.join(gemfile).exist?
        current_dir
      elsif current_dir.root?
        raise UnknownProject.new(Dir.pwd)
      else
        find_project_root(current_dir.parent)
      end
    end
  1. Otherwise we could try just walking up the current path to find the first one that defines config/application.rb?
    def find_project_root(current_dir)
      if current_dir.join('config/application.rb').exist?
        current_dir
      elsif current_dir.root?
        raise UnknownProject.new(Dir.pwd)
      else
        find_project_root(current_dir.parent)
      end
    end
  1. To support engines as well we could just walk up the tree and find the first directory that contains a Gemfile.lock
    def find_project_root(current_dir)
      if current_dir.join('Gemfile.lock').exist?
        current_dir
      elsif current_dir.root?
        raise UnknownProject.new(Dir.pwd)
      else
        find_project_root(current_dir.parent)
      end
    end

Any other ideas?

I can submit a PR if you are happy with any of these proposals.

@deivid-rodriguez
Copy link
Contributor

Just to make it clear, I don't maintain this repo. I just noticed this issue and thought the pointer to the regression in 2.1.1 could be helpful.

@JuPlutonic
Copy link

JuPlutonic commented Apr 8, 2021

@chriscz my coin: 3rd needs to be much more complex:

if current_dir.join('Gemfile.lock').exist? || current_dir.join('gems.locked').exist?

@chriscz
Copy link

chriscz commented Apr 9, 2021

Come to think of it, just looking for Gemfile + application config sounds like a good solution @JuPlutonic, should actually work for the dummy-app case as well.

@sdcorey
Copy link

sdcorey commented Oct 2, 2024

I ran across this recently as well. It's only a minor annoyance for our purposes. All the above discussion holds regarding the cause: Spring::gemfile is returning a fully qualified pathname, and Spring::find_project_root, which recurses the directory tree looking for project root, is expecting the basename only -- no path. Spring::gemfile also contains special code to handle ruby 1.9.x which will cause a failure (with a different message) if the CWD is anything other than project root. All code is contained in "spring/lib/spring/configuration.rb".

It seems some options are:

A. Drop support for starting spring in a directory other than project root. Fail with a meaningful error if this is attempted. Spring#find_project_root goes away.

B. Create Spring::gemfile_basename which returns the correct gemfile basename for 1.9.x and other versions. This is clearly what the current implementation of Spring#find_project_root is expecting in order to work. This keeps Spring::gemfile from being referenced in the recursion loop. Once the recursion is complete, Spring::gemfile can then use the found project root to provide a correct gemfile path for other parts of the code that rely on it.

C. Drop support for ruby 1.9.x and trust Bundler::default_gemfile to find the root path for us if indeed it does so robustly across its intended compatible versions. Spring#find_project_root goes away.

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

5 participants