How to run bundle from Ruby script and capture errors - ruby

I have an error sometimes when running bundle install
Bundler::GemspecError: Could not read gem at /path/to/cache/gem It may be corrupted
I have a web URL filter that seems to block the initial attempt at downloading the gem (if I delete the cache file in question and run again, it works). Furthermore, the contents of the cache file is the html from the web URL filter page.
I would like to not have to delete the cache file and re-run, I would like to have bundler automatically rerun if this scenario happens.
I have thought about running bundle from a Ruby script, but I cannot seem to capture the error.
I need to automate the build of my project in Docker.
begin
puts "Starting bundle install"
system %w[bundle install]
rescue Bundler::GemspecError => e
puts e
end
However, I cannot seem to rescue the exception; the error thrown is:
Bundler::GemspecError: Could not read gem at /Users/lewis/.rvm/gems/ruby-2.3.3#hendricks-offline/cache/rack-2.0.3.gem. It may be corrupted.
An error occurred while installing rack (2.0.3), and Bundler cannot continue.
Make sure that `gem install rack -v '2.0.3'` succeeds before bundling.
The exception is not captured, as I get no output. I have been advised that this is because i am running bundle now outside of Ruby world, so to speak.
How would I go about this?

You can't rescue from system commands as it's not something fatal for ruby should the command fail, instead you have to check if the system command succeeded or failed using an if condition.
It would be more like:
puts "Starting bundle install"
if system('bundle install')
puts 'bundle successful'
else
puts 'bundle failed, deleting cache file and retrying'
system('command to delete cache file goes here')
system('bundle install')
end

Related

`bundler install` throwing GemNoutFound error while `bundle install` works fine on same Gemfile

I have am trying to run bundler and/or bundle commands against a Gemfile.
Whenever I try any of the bundler commands, it throws an error. For example, if I run bundler install, I get the following error
Could not find gem gem_name in any of the gem sources listed in your Gemfile.
But if I run any of the bundle commands, like bundle install, it will execute successfully. And I cannot figure out the reason behind this..
So here is what my Gemfile looks like.
source 'https://rubygems.org'
gem 'byebug'
Here is my bundler env output.
## Environment
Bundler 2.0.2
Platforms ruby, x86_64-darwin-18
Ruby 2.6.3.p62 (2019-04-16 revision 67580) [x86_64-darwin18]
Full Path /path/to/home/.rvm/rubies/ruby-2.6.3/bin/ruby
Config Dir /path/to/home/.rvm/rubies/ruby-2.6.3/etc
RubyGems 3.0.4
Gem Home /path/to/home/.rvm/gems/ruby-2.6.3
Gem Path /path/to/home/.rvm/gems/ruby-2.6.3:/path/to/home/gems/ruby-2.6.3#global
User Path /path/to/home/.gem/ruby/2.6.0
Bin Dir /path/to/home/.rvm/gems/ruby-2.6.3/bin
Tools
Git 2.20.1 (Apple Git-118)
RVM 1.29.9 (latest)
rbenv not installed
chruby not installed
rubygems-bundler (1.4.5)
.. some other info and no Gemfile.lock found since I don't have it yet
My bundle env output is obviously the same as above.
So I did a lot of digging around bundler source code and figured some things out.
When I run bundle lock, I eventually reach the file ruby_executable_hooks.rb in Bin Path above.
Then Gem::ExecutableHooks.run($0) gets run and eventually reaches the file noexec.rb which returns to ruby_executable_hooks.rb saying
Noexec - skipped binary: bundle.
Note here that no #index is set for Bundler::Definition object, since we never move out of the file noexec.rb. This is because we never get past the if-check that reads
if %w(bundle rubygems-bundler-uninstaller).includes?(bin).
Now the control will move down ruby_executable_hooks.rb and run
eval content, binding, $0 and try to run the command. This time, #allowed_remote is true in rubygems.rb and Bundler::Index instances actually communicate with remote rubygems.org to get the info it is looking for.
After that things go smoothly and Gemfile.lock is generated.
But when I run bundler lock, things start out the same until we reach noexec.rb. Here we get past the if check that ended the flow for bundle lock and call the setup method defined in noexec.rb, which calls the candidate? method. This dispatches the control flow to specs method defined in definition.rb.
It follows the same flow that bundle lock went through in its eval... call, except that now we entered specs method straight without calling resolve_remotely!, which sets the #allowed_remote instance variable to true for Bundler::Source objects.
So when specs method of rubygems.rb is called as source.specs inside index method of defintion.rb, we are not allowed to fetch things remotely and add them to the Bundler::index object.
Eventually, calling search method on the index object doesn't return what it would for bundle lock and we never make it past verify_gemfile_dependencies_are_found method in resolver.rb. This method raises the exact error statement I put above near line 280-ish in resolver.rb. When it is raised, we return to ruby_executable_hooks again.
Then we go the eval.. route as bundle lock command did. This time, #allow_remote is true. But we are prevented from modifying #index variable inside index method defined in definition.rb because it is already built in the previous run. We never make it past the code
#index ||= Index.build do .. since #index is not nil.
Rest is the same as our first run that got stopped at verify_gemfile_dependencies_are_found.
I am very confused because I always thought bundler just loads bundle bin and takes the same route from there. But apparently that is not the case and there are indeed subtle differences.
Am I not setting something correctly in my environment? Please help!
Don't use bundler - use bundle. It's in the Bundler documentation, on the landing page of their site.

Gmail gem error when run inside Rspec - 'cannot load such file -- gmail/client'

I'm using this gem called gmail and it works perfectly when run in just a plain ruby script. However, when used inside Rspec, I keep getting this error.
Failure/Error: #gmailnew = Gmail.new()
LoadError:
cannot load such file -- gmail/client
# /Library/Ruby/Gems/2.0.0/gems/gmail-0.6.0/lib/gmail.rb:50:in new'
# ./temp.rb:7:inblock (2 levels) in '
Any idea what causes this?
- I've already tried reinstalling it since it seems to be not finding a file.
I could not figure out what causes the conflict, similar problems by others.
Why is autoload failing to load files for gems
Ruby autoload conflicts between gmail and parse_resource gems
I was able to resolve this by using Bundler.
1) Added gem inside the Gemfile (gem 'gmail')
2) $ bundle install - this installs all gems inside the Gemfile, and avoids the conflicts
3) $ bundle exec rspec my_spec_file.rb - executes Rspec

Local ruby gems fail to install on Windows 2008R2: Errno::EADDRNOTAVAIL

Edit: Bug in rubygems 2.4.4. (fixed in 2.4.5)
I'm having trouble installing gems with the embedded ruby that comes with Chef Client v12.2.1, using the chef_gem resource:
Mixlib::ShellOut::ShellCommandFailed
------------------------------------
chef_gem[zabbixapi] (generic_server_win::libzabbix-deps line 6) had an error: Mixlib::ShellOut::ShellCommand Failed: Expected process to exit with [0], but received '1'
---- Begin output of C:/opscode/chef/embedded/bin/gem install c:/chef/cache/zabbixapi-2.2.2.gem -q --no-rdoc --no-ri -v "2.2.2" ----
STDOUT:
STDERR: ERROR: While executing gem ... (Errno::EADDRNOTAVAIL)
The requested address is not valid in its context. - connect(2)
---- End output of C:/opscode/chef/embedded/bin/gem install c:/chef/cache/zabbixapi-2.2.2.gem -q --no-rdoc --no-ri -v "2.2.2" ----
Ran C:/opscode/chef/embedded/bin/gem install c:/chef/cache/zabbixapi-2.2.2.gem -q --no-rdoc --no-ri -v "2.2.2" returned 1
Also:
Same result when running the command manually on the command-line as an Administrator with the --local option
Same error occurs for other gems.
The servers that I'm trying to run this on have no internet access
I'm unable to reproduce the problem on a newly installed test machine (with internet access)
The version of ruby used is 2.0.0: ruby 2.0.0p451 (2014-02-24) [i386-mingw32]
Here is my Chef recipe:
cookbook_file "#{Chef::Config[:file_cache_path]}/zabbixapi-2.2.2.gem" do
source 'zabbixapi-2.2.2.gem'
end
chef_gem "zabbixapi" do
source "#{Chef::Config[:file_cache_path]}/zabbixapi-2.2.2.gem"
end
You're problem is that the chef_gem resource is special in the way it enforce the use of the embedded ruby in chef installation and that it is run before convergence to allow the gem to be required in recipes. documentation about it here
To use a local source deployed with chef you have to ensure the file is present before, if not the gem command will try to download it (and fail with no internet access).
To ensure your local file is present before the chef_gem call you have to ensure the cookbook_file resource is called at compile time with this trick
in your specific case this should do:
cookbook_file "#{Chef::Config[:file_cache_path]}/zabbixapi-2.2.2.gem" do
action :nothing
source 'zabbixapi-2.2.2.gem'
end.run_action(:create)
chef_gem "zabbixapi" do
source "#{Chef::Config[:file_cache_path]}/zabbixapi-2.2.2.gem"
end
The action nothing in the resource is to avoid having it called twice (once in compile phase and once in converge phase, even if the later won't have any impact, it's better to save time no evaluating it twice) then calling the action :create at end of the definition will trigger the action in the compile phase and the file will be present for the chef_gem call later.

Bundler and wrong binstubs?

I run rails s or bundle exec rails s and I get this warning:
Bundler is using a binstub that was created for a different gem.
This is deprecated, in future versions you may need to `bundle binstub rails` to work around a system/bundle conflict.
What does this mean? From looking around the bundler site, my understanding of binstubs is that you can set executables to them, so instead of running bundle exec blabla you can just do bin/blabla. So this error is saying my bundler isn't set to the right binstub?
When I run the bundle binstub rails I get this output
rails has no executables, but you may want one from a gem it depends on.
railties has: rails
bundler has: bundle, bundler
I do not understand what my system is trying to tell me, and it's not breaking anything, but I have a hunch this could turn into a bigger issue if I don't fix it
ruby 2.0.0p247
which ruby
/Users/evan/.rvm/rubies/ruby-2.0.0-p247/bin/ruby
which bundler
/Users/evan/.rvm/gems/ruby-2.0.0-p247/bin/bundler
Rails 4.0.2
Edit:
So, if I run the commands in the nag message:
bundle config --delete bin # Turn off Bundler's stub generator
rake rails:update:bin # Use the new Rails 4 executables
I end up getting uninitialized constant Bundler errors with bundle exec commands and the only way I've found to fix that is to rerun bundle install --binstubs which brings back the nag message at the start of this post.
What worked for me was
rm -rf bin/*
Then open a new terminal session and
bundle exec rake app:update:bin
Solution in my case: - Other solutions didn't work for me.
In your Rails directory:
mv /usr/bin/rails /usr/bin/rails.old
bundle config --delete bin
rm -rf bin
# for rails 4.x:
rake rails:update:bin
# for rails 3.x:
bundle install --binstubs
# if you're using rbenv
rbenv rehash
rm -rf ~/.rbenv/plugins/{rbenv-bundle-exec,rbenv-gemset,bundler}
Also be sure that bin/rails is added to the path like:
PATH=./bin:$PATH
Good luck.
This error may raise when you update your ruby but not the related gems.
To check if this is your case, try to make a new rails app in a new empty directory (to be sure RVM is not autoloading any gemset)
make /tmp/test && cd test && rails new test
If this fails stating that it cannot find a suitable 'rails', then simply run
gem update
and overwrite any conflicting rails.
gem uninstall bundler
gem install bundler
Uninstalling all my versions of Bundler, and then installing the latest version fixed it for me. I had multiple versions of bundler installed, so when I ran bundle exec rails s I think the wrong Bundler was used, giving me the warning message.
You may need to generate new stubs after reinstalling Bundler, but I didn't have to.
I was able to fix this by looking at the commit history for bin/rails using git log -p bin/rails
The current, error producing content is:
#!/usr/bin/env ruby
#
# This file was generated by Bundler.
#
# The application 'rails' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
load Gem.bin_path('railties', 'rails')
The original, non-error content was:
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'
When I restored the original bin/rails content, the warning message disappeared. Previous attempts had returned uninitialized constant Bundler errors on all bundle exec commands, but now they work. It's worth noting that the original content appears to be exactly what rails new blabla generates in rails 4.0.x.
Still, I would like to know why the first code block causes issues because it's exactly what bundle install --binstubs generates.
Edit: turns out this solution does not work. Pushed this fix to a heroku staging server and heroku errors on startup: all bin/rails commands throw uninitialized constant Bundler and heroku starts up with bin/rails server ..... so this is a not really a fix.
Edit2:
If I add these two lines to the second block (the original bin/rails content), all bin/rails commands are working again:
require 'rubygems'
require 'bundler/setup'
My guess is that the second line is what fixes the bundler errors I was having.
Interestingly, when I tried to edit the first block of code in this post to try and debug which line was throwing the warning, any change I made caused all rails commands to failure, with no information except for the nag note in the OP. weird.
Final bin/rails that fixed my issue:
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rubygems'
require 'bundler/setup'
require 'rails/commands'
Any additional insight from people who find this would be welcome!

After installing a gem within a script, how do I load the gem?

I have a small Ruby script that I'm writing to automate the preparation of a development environment on local machines. Because I can't be certain that the rubyzip2 library is present on all of the machines, I'm having the script install it when needed.
Currently, my script is doing the following:
begin
require 'zip/zip'
rescue LoadError
system("gem install rubyzip2")
end
Once the gem has been installed, the script continues execution; however, the gem hasn't been loaded so all code requiring rubyzip2 halts the execution.
How do I load the gem into memory so that the script can continue running after installation?
Instead of doing require 'thegem' and rescuing error, you should check the gem availability before, and then, if needed, install it. After, you can require it.
Take a look at this post for the gem availability
Or this post
EDIT
After installation, you need to clear gem paths if you don't want to reload your script.
You could achieve this with this method :
Gem.clear_paths
There are already answered questions here
So your code should looks like this ( for example ) :
begin
gem "rubyzip2"
rescue LoadError
system("gem install rubyzip2")
Gem.clear_paths
end
require 'zip/zip'
With bundler version higher than 1.10 (to update just run gem install bundler) you can use its new 'inline' feature as described here.
require 'bundler/inline'
gemfile(true) do
source 'https://rubygems.org'
gem 'catpix'
end
puts Catpix::VERSION
First parameter of gemfile method is whether gems that aren't already installed on the user's system should be installed.
Use Bundler with your application/library. This will allow users to run bundle and all the gems will be fetched and ready for you to use.
Ok so you may want to use Bundler and set up a Gemfile then have bundler do a bundle install, bundler will fetch out all the gems and install them if it is not already installed and you can then require all the gems in the gem file. Read the documentation in the link for more information.
But what you are looking to do specifically in your question is to use the retry keyword. This keyword will retry the loop after the rescue was called.
So if you require the gem and it fails and the Load Error Exception is called. The Begin Block will rescue, the system call will install the gem, then it will retry and require the gem. Just cautious because this may lead to an infinite loop unless you want to set up a condition to maybe retry it only once.
begin
require 'zip/zip'
rescue LoadError
system("gem install rubyzip2")
retry
end

Resources