Chef with omnibus using 3rd party gems - ruby

Could someone please help with a question regarding use of a 3rd party gem with an omnibus installation running Chef recipes?
I would like to use a 'p4ruby' gem that has been packaged up with omnibus and should be available for use in a recipe using
require 'P4'
I get the error
cannot load such file -- P4
In the installation directory I can see
./embedded/service/gem/ruby/2.1.0/gems/p4ruby-2015.1.0/ext/P4/p4rubyconf.h
./embedded/service/gem/ruby/2.1.0/gems/p4ruby-2015.1.0/ext/P4/p4rubydebug.h
./embedded/service/gem/ruby/2.1.0/cache/p4ruby-2015.1.0.gem
./embedded/service/gem/ruby/2.1.0/specifications/p4ruby-2015.1.0.gemspec
./embedded/service/gem/ruby/2.1.0/build_info/p4ruby-2015.1.0.info
./embedded/lib/ruby/gems/2.1.0/gems/p4ruby-2015.1.0/ext/P4/p4rubyconf.h
./embedded/lib/ruby/gems/2.1.0/gems/p4ruby-2015.1.0/ext/P4/p4rubydebug.h
./embedded/lib/ruby/gems/2.1.0/cache/p4ruby-2015.1.0.gem
I have read through the Chef docs and see that I could use chef_gem or gem_package for example
gem_package 'p4ruby' do
end
However this always tries to compile the gem and this leads to errors in our target installation environment as we cannot expect dev tools such as make and g++ to be available and we do not want to force installation of them.
How can I get Chef to 'see' this gem or to specifiy a 3rd party gem without compilation running? Is this possible?
For further info if I do use gem_package on a machine that does have make and g++ I end up with the following in the install directory (which looks slightly different than before). Is this a path issue?? I've searched all over but cannot find a solution
./embedded/service/gem/ruby/2.1.0/gems/p4ruby-2015.1.0/ext/P4/p4rubyconf.h
./embedded/service/gem/ruby/2.1.0/gems/p4ruby-2015.1.0/ext/P4/p4rubydebug.h
./embedded/service/gem/ruby/2.1.0/cache/p4ruby-2015.1.0.gem
./embedded/service/gem/ruby/2.1.0/specifications/p4ruby-2015.1.0.gemspec
./embedded/service/gem/ruby/2.1.0/build_info/p4ruby-2015.1.0.info
./embedded/lib/ruby/gems/2.1.0/gems/p4ruby-2015.1.0/ext/P4/p4rubyconf.h
./embedded/lib/ruby/gems/2.1.0/gems/p4ruby-2015.1.0/ext/P4/p4rubydebug.h
./embedded/lib/ruby/gems/2.1.0/cache/p4ruby-2015.1.0.gem
./embedded/lib/ruby/gems/2.1.0/specifications/p4ruby-2015.1.0.gemspec
Many thanks for any help

How can I get Chef to 'see' this gem or to specifiy a 3rd party gem
without compilation running? Is this possible?
No, if a gem has some C extension, it must be compiled and will need build tools, that's why there's the build-essential cookbook.
The differences between chef_gem and gem_package are this:
gem_package will install to system ruby (or default to chef embedded ruby is none present) where chef_gem will always install in chef embedded ruby.
gem_package run in converge phase where chef_gem run at compile time
The main reason is that chef_gem is aimed at installing gem to be used in recipes where gem_package is aimed at managing system gems for use out of chef.

Think I found a way of doing this...
$:.unshift *Dir[File.path('<my_path_to_gems>/**/lib')]
I can then
require 'P4'
in my recipes

Related

How does Homebrew check for installed gems?

I have a formula that depends on a gem. It has depends_on 'some-gem' => :ruby at the top. I am sure it is installed, since I can use it as intended and it’s there when running gem list. However, when trying to install the formula, it shows
my-formula: Unsatisfied dependency: some-gem
Homebrew does not provide Ruby dependencies; install with:
gem install some-gem
Error: An unsatisfied requirement failed this build.
So, how does Homebrew check for installed gems?
The actual check that is run to detect installed gems and similar things is in /usr/local/Library/Homebrew/requirements/language_module_dependency.rb, which in the case of Ruby does:
/usr/bin/env ruby -rubygems -e require\ 'GEMNAME'
Note that this calls ruby from the path, so it could be affected by mixups there. Make sure you have the right ruby first in your path, have the right gem program that goes with ruby, and possibly add some debugging to your formula to see what environment it is running under.

Is it possible to install and use a Ruby distribution alongside a PC with Opscode Chef Client without one affecting the other?

I'm running chef-client on Windows, and as it happens, the Ruby distribution (1.9.3) embedded in Chef-Client is available system-wide. I believe this is necessary in order to have chef-client function properly? I have tried installing another Ruby distribution (namely, v2.0) but that seems to break Chef-Client.
What I was hoping to achieve is to have chef-client install & configure a Ruby app written against v2.0, so that I can run that app with v2 but have chef-client continue to use the embedded Ruby distribution.
Would that not be possible?
Sure, that's why Opscode is using their Omnibus packages, which ships everything required to run Chef.
Use the "gem_binary" attribute to specify the ruby distro:
gem_package "somegem" do
gem_binary "/usr/bin/gem"
action :install
end
The documentation describe the parameter as follows:
gem_binary: An attribute for the gem_package provider that is used to specify a gems binary. This attribute is useful when installing Ruby 1.9 gems while running in Ruby 1.8. By default, the same version of Ruby that is used by the chef-client will be installed.

build a ruby gem and conditionally specify dependencies

I am working on a gem that has needs to set dependencies conditionally when the gem is installed. I've done some digging around
and it looks like i'm not alone in this need.
Rubygems: How do I add platform-specific dependency?
this is a long thread
http://www.ruby-forum.com/topic/957999
The only way I can see to add dependencies to a gem is to use add_dependency method within a Gem::Specifiction block in a .gemspec file
Gem::Specification.new do |s|
# ... standard setup stuff
# conditionally set dependencies
s.add_dependency "rb-inotify", "~> 0.8.8" if RUBY_PLATFORM =~ /linux/i
s.add_dependency "rb-fsevent", "~> 0.4.3.1" if RUBY_PLATFORM =~ /darwin/i
s.add_dependency "rb-fchange", "~> 0.0.5" if RUBY_PLATFORM =~ /mswin|mingw/i
end
Based on all of the docs and threads I found on the net, I would have expected that if you install the gem on
Linux, then, rb-inotify would be a dependency and auto-installed
Mac - rb-fsevent would be installed
Windows - rb-fchange would be installed
However, it seems that is not the case. The "if" statements within the block are evaluated at the time the gem is built and packaged. Therefore,
if you build and package the gem on Linux, then, rb-inotify is added as a dependency, Mac, then, rb-fsevent, Windows - rb-fchange.
Still needing a solution, I dug around in the rubygems code and it seems the following is a broad stoke of what happens.
build all of your code for your gem: foo.gem
create a foo.gemspec file
build, package, and release the gem to a gem server such as rubygems.org
let everyone know
developers install it locally via: gem install foo
the foo.gem file is downloaded, unpacked, and installed. all dependencies are installed as well.
everything should be set and we can beging using the gem.
It seems that when the gem is built and released the foo.gemspec file is loaded and the Gem::Specification block is evaluated and converted to YAML, compressed as
metadata.gz, and included in foo.gem. The ruby code is compressed into data.tar.gz and included as well. When the gem is installed on the local developer machine,
the YAML is extracted from metadata.gz and converted back into a Gem::Specification block, however, it is not converted back to the original block.
instead, you will see something like the following:
Gem::Specification.new do |s|
if s.respond_to? :specification_version then
s.specification_version = 3
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
s.add_runtime_dependency(%q<rb-inotify>, ["~> 0.8.8"])
else
s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])
end
else
s.add_dependency(%q<rb-inotify>, ["~> 0.8.8"])
end
end
Ok. So, I have a bird's eye view of the process, however, that does not change my desire to build a single gem and conditionally specify dependencies for a range of OS targets.
If anyone has a solution other than building multiple .gemspec files for each target OS... I'm all ears!!
I have also stumbled upon this problem in the past. The only workaround I could find was to create a Rake task for installing the dependencies. Of course, at that stage, you might just want to let the user figure out on his own which gem he is missing based on the error message he is receiving. In my case, there were several platform-dependent dependencies to be installed, so that wasn't an option.
Rakefile:
task :install do |t|
require './lib/library/installer'
Library::Installer.install
end
Installer:
module Library::Installer
require 'rubygems/dependency_installer'
def self.install
installer = Gem::DependencyInstaller.new
dependency = case RUBY_PLATFORM
when /darwin/i then ["rb-fsevent", "~> 0.4.3.1"]
when /linux/i then ["rb-inotify", "~> 0.8.8"]
when /mswin|mingw/i then ["rb-fchange", "~> 0.0.5"]
end
installer.install(*dependency)
end
Then, the user can use rake install to get install appropriate dependencies.
Conditional dependency install (not just based on platform, but based on user input, for example) is cruelly missing to RubyGems. Let's hope it'll get implemented in the future!
i have never done this myself, but there are some gems that are available in platform specific versions: http://rubygems.org/gems/libv8/versions
from what i understand it's just a naming thing, which can be configured by setting the platform option of your gemspec. have a look at the doc: http://guides.rubygems.org/specification-reference/#platform=
I have looked into this as well and have come to the conclusion that is not possible by design. Having a single 'mega gem' for all platforms causes the problem of not knowing if a platform is supported until the gem is downloaded and installed. A Gem would have to be smart enough to determine what is correct way to install depending on the platform. If a platform is not supported at all, the gem may fail horribly, opening a big can of worms. There use to be a callback after a gem was installed that was removed for the same reason, no magic to get a gem to install correctly. Some people have hacked around this using mkmf, but I suggest following the worn path of a gem per platform as the better solution.
Based on this, in a project that builds a gem for ruby and jruby, I have to manually create each gem and upload them to RubyGem. Using Jeweler this is as simple as specifing the Gemfile, but I have to rebuild the gem spec each time I package a gem. Fairly trivial when supporting only 2 platforms, but the build process is straight forward enough that it could be automated to provide support multiple platform gems.

What is a Ruby gem?

I've searched on Google, and I just found the uses of gem. As in, gem install, etc.
Are gems collections of .rb scripts?
If I build a series of scripts, for example that wraps the functionality of Google translate, is the preferred way of distributing that for usage a gem?
If not, how would I distribute this code?
According to RubyGems Wiki - RubyGems is a package manager for the Ruby programming language that provides a standard format for distributing Ruby programs and libraries (in a self-contained format called a "gem"), a tool designed to easily manage the installation of gems, and a server for distributing them.
The gem command is used to build, upload, download, and install Gem packages.
Gem Usage
RubyGems is very similar to apt-get, portage, and yum in functionality.
Installation:
gem install mygem
Uninstallation:
gem uninstall mygem
Listing installed gems:
gem list --local
Gem Package Building
The gem command may also be used to build and maintain .gemspec and .gem files.
Build .gem from a .gemspec file:
gem build mygem.gemspec
For more info, refer to RubyGems Manuals.
Here are some nice tutorials :)
http://railscasts.com/episodes/135-making-a-gem
http://railscasts.com/episodes/245-new-gem-with-bundler
A gem is a module/Library that you can install and use in every project on your server.
A plugin is a module/Library that you can use inside your project
Indeed, if you make some code what you like to share you can make a gem or plugin of it. You can publish it on for example github.com. You can check the source of the existing gems on github if you like to know how to make a gem as well.
Gem Package Building
Step : gem build your_gem_name.gemspec
simple steps follow click here

How to quickly initialize ruby project development environment?

How to specify gem dependencies in a way that user with only ruby, rake and rubygems installed could issue a single rake command to install all the dependencies required? Is it possible to use the same dependency specification when building gem with GemBuildTask?
It's actually pretty easy to set up a rake task that installs a bunch of gems:
task :install_gems do
require "rubygems"
require "rubygems/dependency_installer"
installer = Gem::DependencyInstaller.new
[["rack"], ["merb-core", "1.0.12"]].each do |args|
installer.install(*args)
end
end
Of course, you could extract this into a method and write a prettier way to specify your dependencies, but this should work great.
I think currently you'd have to write a custom rake task that talked to the Gem library.
It's possible that rip, the (very) new kid on the block, will make it all easier, but it's very early days.
But someone else may have a better way...
If your app is packaged as a gem, you could add the dependencies to the gemspec and rubygems will attempt to install them for you when you install the gem.
There are a bunch of ways to make a gem out of some ruby code. Recently I have taken to using jeweler.
With it, you can install a project as a gem by running rake install. There are some instructions on how to do dependencies on its github wiki.

Resources