How to List Ruby Production Only Dependencies Using Gemfile.Lock and Lockfileparser Class

How to list ruby production only dependencies using Gemfile.lock and LockfileParser class

I will leave my other answer in case it helps someone else: Here is the revised version based on what I think you want

require 'bundler'

class DependencyTree
attr_reader :definition
def initialize(gemfile,lockfile)
@gemfile = gemfile
@definition = Bundler::Definition.build(gemfile,lockfile,nil)
end

def all_dependencies
return @all_dependencies if @all_dependencies
collect_dependencies
end

def inspect
"#<#{self.class.name}:#{self.object_id} Gemfile: #{Pathname.new(@gemfile).expand_path} >"
end

def lock_file
@definition.locked_gems
end

def to_h
lock_file.specs.each_with_object(Hash.new {|h,k| h[k] = []}) do |lock,obj|
gem_file_dep = all_dependencies.detect {|dep| dep[:name] == lock.name} || {group: :unknown}
name = lock.full_name.dup
name << " (#{gem_file_dep[:error]})" if gem_file_dep[:error]
obj[gem_file_dep[:group]] << name
end
end

private
def groupify(dep)
dep.groups.map do |g|
a = [{group: g, name: dep.name}]
begin
a << runtime_dependencies(g,dep.to_spec)
rescue Gem::LoadError => e
a[-1] = {group: g, name: dep.name,error: 'NOT INSTALLED'}
end
end
end
def collect_dependencies
@all_dependencies = @definition.dependencies.map do |dep|
groupify(dep)
end.flatten
group_missing
@all_dependencies.uniq!
end
def runtime_dependencies(group,spec)
spec.dependencies.select { |dep| dep.type == :runtime}.map do |dep|
a = {group: group, name: dep.name}
dep.to_spec.dependencies.empty? ? a : [a] << runtime_dependencies(group,dep.to_spec)
end
end
def group_missing
all_locks.cycle(2) do |a|
deep_dep = @all_dependencies.find_all {|h| a.include?(h[:name])}.uniq
a.each do |k|
deep_dep.each do |h|
all_dependencies << {group: h[:group], name: k, error: 'NOT INSTALLED'}
end
end
end
end
def all_locks
lock_file.specs.map do |spec|
spec.to_lock.delete(' ').split("\n").map do |s|
s.slice(/^[\w\-]+/)
end
end
end
end

the usage is:

 dt = DependencyTree.new('Gemfile','Gemfile.lock') 
dt.to_h

output Snippet:

{:default=>
["actionmailer-4.2.5.2 (NOT INSTALLED)",
"actionpack-4.2.5.2",
"actionview-4.2.5.2",
"activejob-4.2.5.2 (NOT INSTALLED)",
"activemodel-4.2.5.2",
"activerecord-4.2.5.2",
"activerecord-sqlserver-adapter-4.2.17",
"activesupport-4.2.5.2",
"arel-6.0.3 (NOT INSTALLED)",
"axlsx-2.0.1 (NOT INSTALLED)",
"binding_of_caller-0.7.2 (NOT INSTALLED)",
"builder-3.2.3",
"coffee-rails-4.1.1 (NOT INSTALLED)",
"coffee-script-2.4.1 (NOT INSTALLED)",
"coffee-script-source-1.12.2 (NOT INSTALLED)",
"concurrent-ruby-1.0.4",
"debug_inspector-0.0.2 (NOT INSTALLED)",
"erubis-2.7.0",
"execjs-2.7.0"],
:development=>
["airbrussh-1.1.2",
"byebug-9.0.6 (NOT INSTALLED)",
"capistrano-3.7.2"],
:doc => ["sdoc-0.4.2 (NOT INSTALLED)"]}

production gems will be in :default development gems would be :default + :development

Parsing direct and indirect Gems of Gemfile.lock file

indirect dependencies were automatically ignored

It would have helped if you'd included the full output. I just ran it myself, and you get:

[{"name"=>"coderay", "version"=>"1.1.3"},
{"name"=>"domain_name", "version"=>"0.5.20190701"},
{"name"=>"http-accept", "version"=>"1.7.0"},
{"name"=>"http-cookie", "version"=>"1.0.4"},
{"name"=>"json", "version"=>"2.5.1"},
{"name"=>"method_source", "version"=>"1.0.0"},
{"name"=>"mime-types", "version"=>"3.3.1"},
{"name"=>"mime-types-data", "version"=>"3.2021.0704"},
{"name"=>"netrc", "version"=>"0.11.0"},
{"name"=>"rest-client", "version"=>"2.1.0"},
{"name"=>"unf", "version"=>"0.1.4"}, # <------ !!!!!!!!!!!!
{"name"=>"unf_ext", "version"=>"0.0.7.7"},
{"name"=>"yaml", "version"=>"0.1.1"}]

So in summary, the indirect dependency of domain_name, i.e. unf, which is presumably what you were referring to, is included further down the list. Your code already works exactly as you intended.

One minor point, though: You can simplify the implementation a little by doing this:

gemlock_array = context.specs.map { |s| {'name' => s.name, 'version' => s.version.to_s} }

In ruby, assigning a temporary array and appending to it within a .each loop is usually sub-optimal. Use map instead, to just construct the array directly.

Figure out which gem is requesting rb-inotify

It turned out that SASS was requesting rb-inotify. It was loading the newest version of SASS, and I had to pin it (and sass-rails) to older versions. What helped me find this was the DependencyTree solution from this SO post.

Where to put test files that get created. Ruby directory structure

Sounds like a perfect candidate for Dir.mktmpdir.

Upgrading from Rails 4.1 to 4.2 Issue with ffi/rb-inotify

This was because I was pulling in the latest version of SASS. When I changed the Gemfile to pin sass and sass-rails to older versions, this problem went away. It was difficult to find the dependency but i used DependencyTree from this SO post to find it.

Rails uninitialized constant Bundler (NameError)

These commands from the bundler FAQ did the trick:

#remove project-specific settings
rm -rf .bundle/

# remove project-specific cached gems and repos
rm -rf vendor/cache/

# remove the saved resolve of the Gemfile
rm -rf Gemfile.lock

and then rebundling with bundle install

edit: heroku deploy was also failing to start up because it starts the server with bin/rails s and my binstubs were all wonky. to fix this: BUNDLE INSTALL --BINSTUBS, again from bundler docs



Related Topics



Leave a reply



Submit