Why does building the same gemspec multiple times create different gem binaries? - ruby

When building a gem via gem build GEMSPEC, the created .gem file is different each time despite nothing in the code or gemspec changing. To demonstrate, I'll use the hola repository that is linked in the Make your own gem page on RubyGems.org:
$ git clone https://github.com/qrush/hola
$ cd hola
$ gem build hola.gemspec -o 1.gem
$ gem build hola.gemspec -o 2.gem
Using diff and sha512sum you can clearly see that the binaries generated are not the same:
$ diff 1.gem 2.gem
Binary files 1.gem and 2.gem differ
$ sha512sum ./*.gem
288a329f69febb51288bea381275688172e2d13fb13ee0868e910e7e0efdaf1e330b158502ce01378eff637f8d18aa8d8c4f6d9c2922deb92a4018ddacdc0624 ./1.gem
cb35b0e1eabfd3d6b0bff698a7d79caf2bcb6e71781134702857fde8619bc09a451284984ac0394a27c7266e5313374d98c2a1502d2ad89a97de1ac10844978c ./2.gem
Why is this happening? Is there any way to get gem build to output the same binary? If not, is there a way to validate the contents of a built gem is the same as another?

A gemfile is basically a tar archive, and tar preserves things like file modification timestamps which will change. See here for a more thorough investigation of this phenomenon.

Related

Install a Ruby gem to an arbitrary prefix

I'd like to install a Ruby gem to an arbitrary prefix. For the sake of exposition, I'm trying to install github-markup; though, I suspect I'm encountering a general issue rather than anything particular to this package.
In the package's source distribution directory, I've done this:
$ gem install --install-dir $HOME/github-markup/lib/ruby/gems --bindir $HOME/github-markup/bin github-markup
And, of course, I've added $HOME/github-markup/bin to the PATH; and I've added $HOME/github-markup/lib/ruby/gems to RUBYLIB.
But, when I try to run the executable, this happens:
$ github-markup my.md
-bash: /usr/local/bin/github-markup: No such file or directory
Clearly I'm missing a piece of the puzzle.

How to find out what executables came from a particular gem?

I am using gem through the terminal CLI.
I know $ gem list lists the installed gems.
But how do I find the names of the actual executables associated with a particular gem?
For example, CocoaPods.
The gem is called cocoapods. (I installed it using $ sudo gem install cocoapods).
But the actual executable is called pod.
At the moment the best thing I found was $ gem contents <gemName> and then looking through the list, specifically any files inside .../bin/ that don't have an extension. But this method does not seem ideal...
Is there a better way?
Ok, I found what I needed:
To list all the executables for a particular gem:
$ gem specification <gemName> executables
(this basically reads the gemspec file)
If you want to list all the executables for all the installed gems, here is the above command in a loop:
gem list --no-versions | while read -r gemName ; do
echo "Executables for $gemName:"
gem specification $gemName executables
done
(Note 1: I copy-pasted this directly in my terminal and hit enter)
(Note 2: This ran for a while, in my case ~30 seconds for ~100 gems)

How to bundle local gem dependancies in IronWorker

I have a Ruby IronWorker which depends on a private gem that isn't published to RubyGems.
Is there a way to merge this local mygemname-0.0.1.gem into my IronWorker in the .worker file?
I'm hoping to be able to specify something the following in myruby.worker:
gem 'mygemname', '>=0.0.1', :path=> 'vendor/bundle'
Currently this give the following error
.rvm/gems/ruby-1.9.3-p0/gems/iron_worker_ng-0.12.2/lib/iron_worker_ng/code/base.rb:79 :in `eval':
wrong number of arguments (3 for 2) (ArgumentError)
Hoping for defaults gives:
gem 'mygemname', '>=0.0.1'
Gives the following error
Could not find gem 'mygemname (>= 0.0.1) ruby' in the gems available on this machine.
Am I on the right track trying to get this to work via the .worker file? Or should I be looking into specifying a custom build step?
If your unpublished gem itself has dependancies, you need to do a little massaging to get things going. Here is a technique that works for me:
mygem.worker
runtime "ruby"
#Merge in an unpublished local gem
dir '../opensource-cli-tools/facebook_exporter', '__gems__/gems'
file '../opensource-cli-tools/facebook_exporter/mygem.gemspec', '__gems__/specifications'
#Merge in a custom build script to fetch the unpublished gem's dependancies
file "Gemfile"
file "install_dependancies.sh"
remote_build_command 'chmod +x install_dependancies.sh && ./install_dependancies.sh'
#Run the puppy!
exec "run.rb"
install_dependancies.sh
echo "Installing dependancies to __gems__/"
gem install bundler --install-dir ./__gems__ --no-ri --no-rdoc
bundle install --standalone --path ./__gems__
cp -R ./__gems__/ruby/*/* ./__gems__
rm -rf ./__gems__/ruby
echo "Fixing install location of mygem"
mv ./__gems__/gems/mygem ./__gems__/gems/mygem-0.0.1
As far as i know, git and local paths unsupported right now.
Here is way to manually include local gem:
Add these lines to .worker file:
dir '../vendor/bundle/mygemname', '__gems__/gems'
file '../vendor/bundle/mygemname/mygemname.gemspec', '__gems__/specifications'

Viewing a Gem's Source Code

Ruby dabbler/newbie here who's not familiar with the ecosystem, so apologies if this is one of those super duh questions.
Is there a way to view all the files and/or source code installed by a gem? That is, I just ran
$ gem install sass
And the sass gem is now a part of my local system
$ gem list --local
...
sass (3.1.16, 3.1.2)
...
I want to know what the gem install command put on my system. Is there a command I can run to see all the files installed by the gem?
After some googling, man gem and gem help commands, I discovered the contents command.
$ gem contents sass
However, when I run this command with the aforementioned sass gem, I get the following results
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/engine_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/functions_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/extend_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/logger_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/css2sass_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/conversion_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/script_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/util/subset_map_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/util/multibyte_string_scanner_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/callbacks_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/importer_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/scss/css_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/scss/scss_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/scss/rx_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/util_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/script_conversion_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/less_conversion_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/cache_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/test/sass/plugin_test.rb
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/bin/sass
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/bin/sass-convert
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.16/bin/scss
However, this list seems incomplete as I know there are files in
.../.rvm/gems/ruby-1.9.2-p180/gems/sass-3.1.2/lib/
Why does contents not show the files from lib?
Is it possible for a gem installer to install files outside of the gems folder?
Is there a command that can show everything installed by a gem?
gem has an unpack command: http://guides.rubygems.org/command-reference/#gem-unpack
gem unpack rake
ls rake-0.4.3/
There are two really good ways to do this. There is another gem which allows you to open the gem and edit. This gem is call gem-open
gem install gem-open
then
gem open sass
Another way is to generate your own rdocs.
gem rdoc sass
You can then look at your rdocs by
gem server
Also if you are using rvm, you can type rvm info and it will show GEM_HOME location.
This will be where all your gems source code is.
cd $GEM_HOME
cd gems/sass-3.1.2/
Update:
This is the way I mostly do this now, when using bundler.
cd $(bundle show sass)
This will be the version of sass in your Gemfile.
I usually open a gem by running this command from the console
EDITOR=<your editor> bundle open <name of gem>
The lib/ directory you mentioned is for version 3.1.2 of the gem; gem contents without --version will just list one version (it appears to pick the newest version, but I'm unable to verify this is always true). What output do you get for gem contents --version 3.1.2 sass?
You can also use just rename the .gem file to .tar and extract as a posix archive. The source code is inside it in the lib folder. See https://blog.srcclr.com/extracting-ruby-source-code-from-gem-packages/ for more details.
In addition to gem contents, another command you might find useful is gem environment. If you have multiple paths for your gem installations, they will all be listed under the "GEM PATHS" label.

How to remove a remote source from gem remote sources

I have added gemcutter.org to my Rubygems sources, and now I do not know how to remove it.
$ gem sources
*** CURRENT SOURCES ***
http://gemcutter.org
http://gems.rubyforge.org/
$ gem sources -r http://gemcutter.org
http://gemcutter.org removed from sources
$ gem sources
*** CURRENT SOURCES ***
http://gems.rubyforge.org/
In general you can find the syntax with
gem help *command*
So this shows the options you needed:
gem help sources
But what if the problem is that the default source causes gem install to hang on non-connected systems? (You think it's impossible?!?) If you run the command provided by the most popular answer, and this happens:
# gem sources -r https://rubygems.org
source https://rubygems.org not present in cache
Then to deal with this situation, you must modify source cringe:
sed -i '/[[:space:]]def self\.default_sources/a return []' \
$RUBYGEMS_DIR/rubygems/defaults.rb
This adds a line of code, namely return [] to return an empty array, after the method that returns "default sources". Run gem environment and you will see no nasty remote sources. Now you can go back and undo the sed command, or manually add the rubygems repo or whatever.
EDIT: Clarify, generalize.

Resources